diff --git a/lib/ProductOpener/APITest.pm b/lib/ProductOpener/APITest.pm index 90193c79bf631..d75c69f290263 100644 --- a/lib/ProductOpener/APITest.pm +++ b/lib/ProductOpener/APITest.pm @@ -385,6 +385,7 @@ my $tests_ref = ( headers => {header1 => value1, } # optional. You may add an undef value to test for the inexistance of a header response_content_must_match => "regexp" # optional. You may add a case insensitive regexp (e.g. "Product saved") that must be matched response_content_must_not_match => "regexp" # optional. You may add a case insensitive regexp (e.g. "error") that must not be matched + sort_products_by => "product_name" # optional. You may provide a field to sort the returned products by so that they are in an expected order } ], ); @@ -565,6 +566,9 @@ sub execute_api_tests ($file, $tests_ref, $ua = undef) { if (ref($decoded_json) eq 'HASH') { if (defined $decoded_json->{'products'}) { normalize_products_for_test_comparison($decoded_json->{'products'}); + if (defined $test_ref->{sort_products_by}) { + sort_products_for_test_comparison($decoded_json->{'products'}, $test_ref->{sort_products_by}); + } } if (defined $decoded_json->{'product'}) { normalize_product_for_test_comparison($decoded_json->{'product'}); diff --git a/lib/ProductOpener/Display.pm b/lib/ProductOpener/Display.pm index 9ef1789ad188c..da6ef84397c40 100644 --- a/lib/ProductOpener/Display.pm +++ b/lib/ProductOpener/Display.pm @@ -83,7 +83,6 @@ BEGIN { &search_and_graph_products &search_and_map_products &display_recent_changes - &add_tag_prefix_to_link &display_taxonomy_api &map_of_products @@ -1525,6 +1524,8 @@ sub generate_query_cache_key ($name, $context_ref, $request_ref) { sub query_list_of_tags ($request_ref, $query_ref) { + add_params_to_query($request_ref, $query_ref); + add_country_and_owner_filters_to_query($request_ref, $query_ref); my $groupby_tagtype = $request_ref->{groupby_tagtype}; @@ -1827,33 +1828,14 @@ sub display_list_of_tags ($request_ref, $query_ref) { . $th_nutriments . "\n\n"; - my $main_link = ''; + # To get the root link, we remove the facet name from the current link + my $main_link = $request_ref->{current_link}; + $main_link =~ s/\/[^\/]+$//; # Remove the last / and everything after ir my $nofollow = ''; if (defined $request_ref->{tagid}) { - local $log->context->{tagtype} = $request_ref->{tagtype}; - local $log->context->{tagid} = $request_ref->{tagid}; - - $log->trace("determining main_link for the tag") if $log->is_trace(); - if (defined $taxonomy_fields{$request_ref->{tagtype}}) { - $main_link = canonicalize_taxonomy_tag_link($lc, $request_ref->{tagtype}, $request_ref->{tagid}); - $log->debug("main_link determined from the taxonomy tag", {main_link => $main_link}) - if $log->is_debug(); - } - else { - $main_link = canonicalize_tag_link($request_ref->{tagtype}, $request_ref->{tagid}); - $log->debug("main_link determined from the canonical tag", {main_link => $main_link}) - if $log->is_debug(); - } $nofollow = ' rel="nofollow"'; } - # add back leading dash when a tag is excluded - if ((defined $request_ref->{tag_prefix}) and ($request_ref->{tag_prefix} ne '')) { - my $prefix = $request_ref->{tag_prefix}; - $main_link = add_tag_prefix_to_link($main_link, $prefix); - $log->debug("Found tag prefix for main_link", {request => $request_ref}) if $log->is_debug(); - } - my %products = (); # number of products by tag, used for histogram of nutrition grades colors $log->debug("going through all tags", {}) if $log->is_debug(); @@ -2044,7 +2026,7 @@ sub display_list_of_tags ($request_ref, $query_ref) { } } - my $product_link = $main_link . $link; + my $tag_link = $main_link . $link; $html .= ""; @@ -2112,13 +2094,13 @@ sub display_list_of_tags ($request_ref, $query_ref) { $css_class =~ s/^\s+|\s+$//g; $info .= ' class="' . $css_class . '"'; - $html .= "" . $display . ""; + $html .= "" . $display . ""; $html .= "\n$products" . $td_nutriments . $extra_td . "\n"; my $tagentry = { id => $tagid, name => $display, - url => $formatted_subdomain . $product_link, + url => $formatted_subdomain . $tag_link, products => $products + 0, # + 0 to make the value numeric known => $known, # 1 if the ingredient exists in the taxonomy, 0 if not }; @@ -2156,7 +2138,7 @@ sub display_list_of_tags ($request_ref, $query_ref) { # In case there are multiple country names and thus links that map to the region # only keep the first one, which has the biggest count (and is likely to be the correct name) if (not defined $countries_map_links->{$region}) { - $countries_map_links->{$region} = $product_link; + $countries_map_links->{$region} = $tag_link; my $name = $display; $name =~ s/<(.*?)>//g; $countries_map_names->{$region} = $name; @@ -2883,8 +2865,8 @@ sub display_points ($request_ref) { my $tagtype = $request_ref->{tagtype}; my $tagid = $request_ref->{tagid}; my $display_tag; - my $newtagid; - my $newtagidpath; + my $new_tagid; + my $new_tagid_path; my $canon_tagid = undef; local $log->context->{tagtype} = $tagtype; @@ -2897,62 +2879,59 @@ sub display_points ($request_ref) { $canon_tagid = canonicalize_taxonomy_tag($lc, $tagtype, $tagid); $display_tag = display_taxonomy_tag($lc, $tagtype, $canon_tagid); $title = $display_tag; - $newtagid = get_taxonomyid($lc, $display_tag); + $new_tagid = get_taxonomyid($lc, $display_tag); $log->debug("displaying points for a taxonomy tag", - {canon_tagid => $canon_tagid, newtagid => $newtagid, title => $title}) + {canon_tagid => $canon_tagid, new_tagid => $new_tagid, title => $title}) if $log->is_debug(); - if ($newtagid !~ /^(\w\w):/) { - $newtagid = $lc . ':' . $newtagid; + if ($new_tagid !~ /^(\w\w):/) { + $new_tagid = $lc . ':' . $new_tagid; } - $newtagidpath = canonicalize_taxonomy_tag_link($lc, $tagtype, $newtagid); - $request_ref->{current_link} = $newtagidpath; + $new_tagid_path = canonicalize_taxonomy_tag_link($lc, $tagtype, $new_tagid); + $request_ref->{current_link} = $new_tagid_path; $request_ref->{world_current_link} = canonicalize_taxonomy_tag_link($lc, $tagtype, $canon_tagid); } else { $display_tag = canonicalize_tag2($tagtype, $tagid); - $newtagid = get_string_id_for_lang($lc, $display_tag); + $new_tagid = get_string_id_for_lang($lc, $display_tag); $display_tag = display_tag_name($tagtype, $display_tag); if ($tagtype eq 'emb_codes') { - $canon_tagid = $newtagid; + $canon_tagid = $new_tagid; $canon_tagid =~ s/-($ec_code_regexp)$/-ec/ie; } $title = $display_tag; - $newtagidpath = canonicalize_tag_link($tagtype, $newtagid); - $request_ref->{current_link} = $newtagidpath; + $new_tagid_path = canonicalize_tag_link($tagtype, $new_tagid); + $request_ref->{current_link} = $new_tagid_path; my $current_lang = $lang; my $current_lc = $lc; $lang = 'en'; $lc = 'en'; - $request_ref->{world_current_link} = canonicalize_tag_link($tagtype, $newtagid); + $request_ref->{world_current_link} = canonicalize_tag_link($tagtype, $new_tagid); $lang = $current_lang; $lc = $current_lc; $log->debug("displaying points for a normal tag", - {canon_tagid => $canon_tagid, newtagid => $newtagid, title => $title}) + {canon_tagid => $canon_tagid, new_tagid => $new_tagid, title => $title}) if $log->is_debug(); } } $request_ref->{current_link} .= "/points"; - if ((defined $tagid) and ($newtagid ne $tagid)) { + if ((defined $tagid) and ($new_tagid ne $tagid)) { $request_ref->{redirect} = $formatted_subdomain . $request_ref->{current_link}; $log->info( - "newtagid does not equal the original tagid, redirecting", - {newtagid => $newtagid, redirect => $request_ref->{redirect}} + "new_tagid does not equal the original tagid, redirecting", + {new_tagid => $new_tagid, redirect => $request_ref->{redirect}} ) if $log->is_info(); redirect_to_url($request_ref, 302, $request_ref->{redirect}); } my $description = ''; - my $products_title = $display_tag; - if ($tagtype eq 'users') { my $user_ref = retrieve("$data_root/users/$tagid.sto"); if (defined $user_ref) { if ((defined $user_ref->{name}) and ($user_ref->{name} ne '')) { $title = $user_ref->{name} . " ($tagid)"; - $products_title = $user_ref->{name}; } } } @@ -2996,196 +2975,86 @@ HEADER return; } -# See issue 1960 -# a tag prefix, such as a minus sign, can indicate that a tag value should be excluded from a query -# during processing this prefix may be removed from the current url link -# this will add the prefix back -# it will put the prefix before the string following the last forward slash in the link -sub add_tag_prefix_to_link ($link, $tag_prefix) { - $link =~ s/^(.*)\/(.*)$/$1\/$tag_prefix$2/; - return $link; -} - -=head2 display_tag ( $request_ref ) +=head2 canonicalize_request_tags_and_redirect_to_canonical_url ($request_ref) -This function is called to display either: - -1. Products that have a specific tag: /category/cakes - or that don't have a specific tag /category/-cakes - or that have 2 specific tags /category/cake/brand/oreo -2. List of tags of a given type: /labels - possibly for products that have a specific tag: /category/cakes/labels - or 2 specific tags: /category/cakes/label/organic/additives - -When displaying products for a tag, the function generates tag type specific HTML -that is displayed at the top of the page: -- tag parents and children -- maps for tag types that have a location (e.g. packaging codes) -- special properties for some tag types (e.g. additives) - -The function then calls search_and_display_products() to display the paginated list of products. - -When displaying a list of tags, the function calls display_list_of_tags(). +This function goes through the tags filters from the request and canonicalizes them. +If the requested tags are not canonical, we will redirect to the canonical URL. =cut -sub display_tag ($request_ref) { - - my $title; - - my $tagtype = $request_ref->{tagtype}; - my $tagid = $request_ref->{tagid}; - my $display_tag; - my $newtagid; - my $newtagidpath; - my $canon_tagid = undef; - - local $log->context->{tagtype} = $tagtype; - local $log->context->{tagid} = $tagid; +sub canonicalize_request_tags_and_redirect_to_canonical_url ($request_ref) { - my $tagtype2 = $request_ref->{tagtype2}; - my $tagid2 = $request_ref->{tagid2}; - my $display_tag2; - my $newtagid2; - my $newtagid2path; - my $canon_tagid2 = undef; + $request_ref->{current_link} = ''; + $request_ref->{world_current_link} = ''; - local $log->context->{tagtype2} = $tagtype2; - local $log->context->{tagid2} = $tagid2; + my $header_meta_noindex = 0; # Will be set if one of the tags is related to a user + my $redirect_to_canonical_url = 0; # Will be set if one of the tags is not canonical - init_tags_texts() unless %tags_texts; + # Go through the tags filters from the request - # Add a meta robot noindex for pages related to users - if ( - ( - (defined $tagtype) - and ($tagtype =~ /^(users|correctors|editors|informers|correctors|photographers|checkers)$/) - ) - or ( (defined $tagtype2) - and ($tagtype2 =~ /^(users|correctors|editors|informers|correctors|photographers|checkers)$/)) - ) - { + foreach my $tag_ref (@{$request_ref->{tags}}) { - $header .= '' . "\n"; - - } + # the tag name requested in url (in $lc language) + my $tagid = $tag_ref->{tagid}; + my $tagtype = $tag_ref->{tagtype}; + # in URLs, tags can be prefixed with a - (e.g /label/-organic) + # to indicate we want to match products without that tag + my $tag_prefix = $tag_ref->{tag_prefix}; + # The tag name displayed in the page (in $lc language) + my $display_tag; + # canonical tag corresponding to tagid + my $canon_tagid; + # normalized tagid, in the $lc language + my $new_tagid; + my $new_tagid_path; - if (defined $tagid) { if (defined $taxonomy_fields{$tagtype}) { $canon_tagid = canonicalize_taxonomy_tag($lc, $tagtype, $tagid); $display_tag = display_taxonomy_tag($lc, $tagtype, $canon_tagid); - $title = $display_tag; - $newtagid = get_taxonomyid($lc, $display_tag); - $log->info("displaying taxonomy tag", {canon_tagid => $canon_tagid, newtagid => $newtagid, title => $title}) + $new_tagid = get_taxonomyid($lc, $display_tag); + $log->info("displaying taxonomy tag", {canon_tagid => $canon_tagid, new_tagid => $new_tagid}) if $log->is_info(); - if ($newtagid !~ /^(\w\w):/) { - $newtagid = $lc . ':' . $newtagid; + if ($new_tagid !~ /^(\w\w):/) { + $new_tagid = $lc . ':' . $new_tagid; } - $newtagidpath = canonicalize_taxonomy_tag_link($lc, $tagtype, $newtagid); - $request_ref->{current_link} = $newtagidpath; - $request_ref->{world_current_link} = canonicalize_taxonomy_tag_link($lc, $tagtype, $canon_tagid); + $new_tagid_path = canonicalize_taxonomy_tag_link($lc, $tagtype, $new_tagid, $tag_prefix); + $request_ref->{current_link} .= $new_tagid_path; + $request_ref->{world_current_link} + .= canonicalize_taxonomy_tag_link($lc, $tagtype, $canon_tagid, $tag_prefix); } else { $display_tag = canonicalize_tag2($tagtype, $tagid); # Use "no_language" normalization for tags types without a taxonomy - $newtagid = get_string_id_for_lang("no_language", $display_tag); - $display_tag = display_tag_name($tagtype2, $display_tag); + $new_tagid = get_string_id_for_lang("no_language", $display_tag); + $display_tag = display_tag_name($tagtype, $display_tag); if ($tagtype eq 'emb_codes') { - $canon_tagid = $newtagid; + $canon_tagid = $new_tagid; $canon_tagid =~ s/-($ec_code_regexp)$/-ec/ie; } - $title = $display_tag; - $newtagidpath = canonicalize_tag_link($tagtype, $newtagid); - $request_ref->{current_link} = $newtagidpath; + $new_tagid_path = canonicalize_tag_link($tagtype, $new_tagid, $tag_prefix); + $request_ref->{current_link} .= $new_tagid_path; my $current_lang = $lang; my $current_lc = $lc; $lang = 'en'; $lc = 'en'; - $request_ref->{world_current_link} = canonicalize_tag_link($tagtype, $newtagid); + $request_ref->{world_current_link} .= canonicalize_tag_link($tagtype, $new_tagid, $tag_prefix); $lang = $current_lang; $lc = $current_lc; - $log->info("displaying normal tag", {canon_tagid => $canon_tagid, newtagid => $newtagid, title => $title}) + $log->info("displaying normal tag", {canon_tagid => $canon_tagid, new_tagid => $new_tagid}) if $log->is_info(); } - # add back leading dash when a tag is excluded - if ((defined $request_ref->{tag_prefix}) and ($request_ref->{tag_prefix} ne '')) { - my $prefix = $request_ref->{tag_prefix}; - $request_ref->{current_link} = add_tag_prefix_to_link($request_ref->{current_link}, $prefix); - $request_ref->{world_current_link} = add_tag_prefix_to_link($request_ref->{world_current_link}, $prefix); - $log->debug("Found tag prefix ", {request => $request_ref}) if $log->is_debug(); - } - - $request_ref->{canon_tagid} = $canon_tagid; - } - else { - $log->warn("no tagid found") if $log->is_warn(); - } - - # 2nd tag? - if (defined $tagid2) { - if (defined $taxonomy_fields{$tagtype2}) { - $canon_tagid2 = canonicalize_taxonomy_tag($lc, $tagtype2, $tagid2); - $display_tag2 = display_taxonomy_tag($lc, $tagtype2, $canon_tagid2); - $title .= " / " . $display_tag2; - $newtagid2 = get_taxonomyid($lc, $display_tag2); - $log->info( - "2nd level tag is a taxonomy tag", - { - tagtype2 => $tagtype2, - tagid2 => $tagid2, - canon_tagid2 => $canon_tagid2, - newtagid2 => $newtagid2, - title => $title - } - ) if $log->is_info(); - if ($newtagid2 !~ /^(\w\w):/) { - $newtagid2 = $lc . ':' . $newtagid2; - } - $newtagid2path = canonicalize_taxonomy_tag_link($lc, $tagtype2, $newtagid2); - $request_ref->{current_link} .= $newtagid2path; - $request_ref->{world_current_link} .= canonicalize_taxonomy_tag_link($lc, $tagtype2, $canon_tagid2); - } - else { - $display_tag2 = canonicalize_tag2($tagtype2, $tagid2); - $newtagid2 = get_string_id_for_lang("no_language", $display_tag2); - $display_tag2 = display_tag_name($tagtype2, $display_tag2); - $title .= " / " . $display_tag2; - - if ($tagtype2 eq 'emb_codes') { - $canon_tagid2 = $newtagid2; - $canon_tagid2 =~ s/-($ec_code_regexp)$/-ec/ie; - } - $newtagid2path = canonicalize_tag_link($tagtype2, $newtagid2); - $request_ref->{current_link} .= $newtagid2path; - my $current_lang = $lang; - my $current_lc = $lc; - $lang = 'en'; - $lc = 'en'; - $request_ref->{world_current_link} .= canonicalize_tag_link($tagtype2, $newtagid2); - $lang = $current_lang; - $log->info( - "2nd level tag is a normal tag", - { - tagtype2 => $tagtype2, - tagid2 => $tagid2, - canon_tagid2 => $canon_tagid2, - newtagid2 => $newtagid2, - title => $title - } - ) if $log->is_info(); - $lc = $current_lc; - } + $tag_ref->{canon_tagid} = $canon_tagid; + $tag_ref->{new_tagid} = $new_tagid; + $tag_ref->{new_tagid_path} = $new_tagid_path; + $tag_ref->{display_tag} = $display_tag; + $tag_ref->{tagtype_path} = '/' . $tag_type_plural{$tagtype}{$lc}; + $tag_ref->{tagtype_name} = lang_in_other_lc($lc, $tagtype . '_s'); - # add back leading dash when a tag is excluded - if ((defined $request_ref->{tag2_prefix}) and ($request_ref->{tag2_prefix} ne '')) { - my $prefix = $request_ref->{tag2_prefix}; - $request_ref->{current_link} = add_tag_prefix_to_link($request_ref->{current_link}, $prefix); - $request_ref->{world_current_link} = add_tag_prefix_to_link($request_ref->{world_current_link}, $prefix); - $log->debug("Found tag prefix 2 ", {request => $request_ref}) if $log->is_debug(); + # We will redirect if the tag is not canonical + if ($new_tagid ne $tagid) { + $redirect_to_canonical_url = 1; } - - $request_ref->{canon_tagid2} = $canon_tagid2; } if (defined $request_ref->{groupby_tagtype}) { @@ -3195,7 +3064,7 @@ sub display_tag ($request_ref) { # If the query contained tags in non-canonical form, redirect to the form with the canonical tags # The redirect is temporary (302), as the canonicalization could change if the corresponding taxonomies change - if (((defined $newtagid) and ($newtagid ne $tagid)) or ((defined $newtagid2) and ($newtagid2 ne $tagid2))) { + if ($redirect_to_canonical_url) { $request_ref->{redirect} = $formatted_subdomain . $request_ref->{current_link}; # Re-add file suffix, so that the correct response format is kept. https://github.com/openfoodfacts/openfoodfacts-server/issues/894 $request_ref->{redirect} .= '.json' if single_param("json"); @@ -3207,152 +3076,128 @@ sub display_tag ($request_ref) { redirect_to_url($request_ref, 302, $request_ref->{redirect}); } - my $weblinks_html = ''; - my @wikidata_objects = (); - if ( ($tagtype ne 'additives') - and (not defined $request_ref->{groupby_tagtype})) - { - my @weblinks = (); - if ((defined $properties{$tagtype}) and (defined $properties{$tagtype}{$canon_tagid})) { - foreach my $key (keys %weblink_templates) { - next if not defined $properties{$tagtype}{$canon_tagid}{$key}; - my $weblink = { - text => $weblink_templates{$key}{text}, - href => sprintf($weblink_templates{$key}{href}, $properties{$tagtype}{$canon_tagid}{$key}), - hreflang => $weblink_templates{$key}{hreflang}, - }; - $weblink->{title} = sprintf($weblink_templates{$key}{title}, $properties{$tagtype}{$canon_tagid}{$key}) - if defined $weblink_templates{$key}{title}; - push @weblinks, $weblink; - } + # Ask search engines to not index the page if it is related to a user + if ($header_meta_noindex) { + $header .= '' . "\n"; + } - if ((defined $properties{$tagtype}) and (defined $properties{$tagtype}{$canon_tagid}{'wikidata:en'})) { - push @wikidata_objects, $properties{$tagtype}{$canon_tagid}{'wikidata:en'}; - } - } + return; +} - if (($#weblinks >= 0)) { - $weblinks_html - .= ''; - } - } +Generate a title from the tags in the request. - my $description = ''; +=head3 Parameters - my $products_title = $display_tag; +=head4 $tags_ref Array of tag filter objects - my $icid = $tagid; - (defined $icid) and $icid =~ s/^.*://; +=head3 Return value - # Gather data that will be passed to the tag template - my $tag_template_data_ref = {}; +Title string. - $tag_template_data_ref->{groupby_tagtype} = $request_ref->{groupby_tagtype}; +=cut - if (defined $tagtype) { +sub generate_title_from_request_tags ($tags_ref) { - # check if there is a template to display additional fields from the taxonomy - # the template is set in the Config.pm file - # This feature was coded before the introduction of knowledge panels - # It is in maintenance mode, and should be reimplemented as facets knowledge panels - # (server side, or with client side facets knowledge panels) + my $title = join(" / ", map {($_->{tag_prefix} // '') . $_->{display_tag}} @{$tags_ref}); - if (exists $options{"display_tag_" . $tagtype}) { + return $title; +} - print STDERR "option display_tag_$tagtype\n"; +=head2 generate_description_from_display_tag_options ($tagtype, $tagid, $display_tag, $canon_tagid) - foreach my $field_orig (@{$options{"display_tag_" . $tagtype}}) { +Generate a description for some tag types, like additives, if there is a template set in the Config.pm file. - my $field = $field_orig; +This feature was coded before the introduction of knowledge panels. +It is in maintenance mode, and should be reimplemented as facets knowledge panels +(server side, or with client side facets knowledge panels) - $log->debug("display_tag - field", {field => $field}) if $log->is_debug(); +=cut - my $array = 0; - if ($field =~ /^\@/) { - $field = $'; - $array = 1; - } +sub generate_description_from_display_tag_options ($tagtype, $tagid, $display_tag, $canon_tagid) { - # Section title? + my $description = ""; - if ($field =~ /^title:/) { - $field = $'; - my $title = lang($tagtype . "_" . $field); - ($title eq "") and $title = lang($field); - $description .= "

" . $title . "

\n"; - $log->debug("display_tag - section title", {field => $field}) if $log->is_debug(); - next; + foreach my $field_orig (@{$options{"display_tag_" . $tagtype}}) { + + my $field = $field_orig; + + $log->debug("display_tag - field", {field => $field}) if $log->is_debug(); + + my $array = 0; + if ($field =~ /^\@/) { + $field = $'; + $array = 1; + } + + # Section title? + + if ($field =~ /^title:/) { + $field = $'; + my $title = lang($tagtype . "_" . $field); + ($title eq "") and $title = lang($field); + $description .= "

" . $title . "

\n"; + $log->debug("display_tag - section title", {field => $field}) if $log->is_debug(); + next; + } + + # Special processing + + if ($field eq 'efsa_evaluation_exposure_table') { + + $log->debug( + "display_tag - efsa_evaluation_exposure_table", + { + efsa_evaluation_overexposure_risk => + $properties{$tagtype}{$canon_tagid}{"efsa_evaluation_overexposure_risk:en:"} } + ) if $log->is_debug(); + + if ( (defined $properties{$tagtype}) + and (defined $properties{$tagtype}{$canon_tagid}) + and (defined $properties{$tagtype}{$canon_tagid}{"efsa_evaluation_overexposure_risk:en"}) + and ($properties{$tagtype}{$canon_tagid}{"efsa_evaluation_overexposure_risk:en"} ne 'en:no')) + { - # Special processing + $log->debug("display_tag - efsa_evaluation_exposure_table - yes", {}) if $log->is_debug(); - if ($field eq 'efsa_evaluation_exposure_table') { + my @groups = qw(infants toddlers children adolescents adults elderly); + my @percentiles = qw(mean 95th); + my @doses = qw(noael adi); + my %doses = (); - $log->debug( - "display_tag - efsa_evaluation_exposure_table", - { - efsa_evaluation_overexposure_risk => - $properties{$tagtype}{$canon_tagid}{"efsa_evaluation_overexposure_risk:en:"} - } - ) if $log->is_debug(); + my %exposure = (mean => {}, '95th' => {}); - if ( (defined $properties{$tagtype}) - and (defined $properties{$tagtype}{$canon_tagid}) - and (defined $properties{$tagtype}{$canon_tagid}{"efsa_evaluation_overexposure_risk:en"}) - and ($properties{$tagtype}{$canon_tagid}{"efsa_evaluation_overexposure_risk:en"} ne 'en:no')) - { + # in taxonomy: + # efsa_evaluation_exposure_95th_greater_than_adi:en: en:adults, en:elderly, en:adolescents, en:children, en:toddlers, en:infants - $log->debug("display_tag - efsa_evaluation_exposure_table - yes", {}) if $log->is_debug(); - - my @groups = qw(infants toddlers children adolescents adults elderly); - my @percentiles = qw(mean 95th); - my @doses = qw(noael adi); - my %doses = (); - - my %exposure = (mean => {}, '95th' => {}); - - # in taxonomy: - # efsa_evaluation_exposure_95th_greater_than_adi:en: en:adults, en:elderly, en:adolescents, en:children, en:toddlers, en:infants - - foreach my $dose (@doses) { - foreach my $percentile (@percentiles) { - my $exposure_property - = "efsa_evaluation_exposure_" . $percentile . "_greater_than_" . $dose . ":en"; - if (!defined $properties{$tagtype}{$canon_tagid}{$exposure_property}) { - next; - } - foreach - my $groupid (split(/,/, $properties{$tagtype}{$canon_tagid}{$exposure_property})) - { - my $group = $groupid; - $group =~ s/^\s*en://; - $group =~ s/\s+$//; - - # NOAEL has priority over ADI - if (exists $exposure{$percentile}{$group}) { - next; - } - $exposure{$percentile}{$group} = $dose; - $doses{$dose} = 1; # to display legend for the dose - $log->debug("display_tag - exposure_table ", - {group => $group, percentile => $percentile, dose => $dose}) - if $log->is_debug(); - } + foreach my $dose (@doses) { + foreach my $percentile (@percentiles) { + my $exposure_property + = "efsa_evaluation_exposure_" . $percentile . "_greater_than_" . $dose . ":en"; + if (!defined $properties{$tagtype}{$canon_tagid}{$exposure_property}) { + next; + } + foreach my $groupid (split(/,/, $properties{$tagtype}{$canon_tagid}{$exposure_property})) { + my $group = $groupid; + $group =~ s/^\s*en://; + $group =~ s/\s+$//; + + # NOAEL has priority over ADI + if (exists $exposure{$percentile}{$group}) { + next; } + $exposure{$percentile}{$group} = $dose; + $doses{$dose} = 1; # to display legend for the dose + $log->debug("display_tag - exposure_table ", + {group => $group, percentile => $percentile, dose => $dose}) + if $log->is_debug(); } + } + } - $styles .= < HTML - ; + ; - foreach my $group (@groups) { + foreach my $group (@groups) { - $table .= ""; - } + $table .= ""; + } - $table .= "\n\n\n\n\n"; + $table .= "\n\n\n\n\n"; - foreach my $group (@groups) { + foreach my $group (@groups) { - $table - .= '"; - } + $table .= '"; + } - $table .= "\n"; + $table .= "\n"; - my %icons = ( - adi => 'moderate', - noael => 'high', - ); + my %icons = ( + adi => 'moderate', + noael => 'high', + ); - foreach my $percentile (@percentiles) { + foreach my $percentile (@percentiles) { - $table - .= ""; - - foreach my $group (@groups) { - - $table .= ""; - } + $table + .= ""; - $table .= "\n"; - } + foreach my $group (@groups) { - $table .= "\n
 " . lang($group) . "" . lang($group) . "
 
 ' . lang($group . "_age") . "' . lang($group . "_age") . "
" - . lang("exposure_title_" . $percentile) . "
(" - . lang("exposure_description_" . $percentile) - . ")
"; - - my $dose = $exposure{$percentile}{$group}; - - if (not defined $dose) { - $table .= " "; - } - else { - $table - .= ''
-										. lang(
-										'; - } - - $table .= "
" + . lang("exposure_title_" . $percentile) . "
(" + . lang("exposure_description_" . $percentile) + . ")
\n"; - - $description .= $table; - - foreach my $dose (@doses) { - if (exists $doses{$dose}) { - $description - .= "

" - . ''
-									. lang( : ' - . lang("additives_efsa_evaluation_exposure_greater_than_" . $dose) - . "

\n"; - } + $table .= ""; + + my $dose = $exposure{$percentile}{$group}; + + if (not defined $dose) { + $table .= " "; + } + else { + $table + .= ''
+								. lang('; } + + $table .= ""; } - next; + + $table .= "\n"; } - my $fieldid = get_string_id_for_lang($lc, $field); - $fieldid =~ s/-/_/g; + $table .= "\n\n"; + + $description .= $table; + + foreach my $dose (@doses) { + if (exists $doses{$dose}) { + $description + .= "

" + . ''
+							. lang( : ' + . lang("additives_efsa_evaluation_exposure_greater_than_" . $dose) + . "

\n"; + } + } + } + next; + } - my %propertyid = (); + my $fieldid = get_string_id_for_lang($lc, $field); + $fieldid =~ s/-/_/g; - # Check if we have properties in the interface language, otherwise use English + my %propertyid = (); - if ((defined $properties{$tagtype}) and (defined $properties{$tagtype}{$canon_tagid})) { + # Check if we have properties in the interface language, otherwise use English - $log->debug("display_tag - checking properties", - {tagtype => $tagtype, canon_tagid => $canon_tagid, field => $field}) - if $log->is_debug(); + if ((defined $properties{$tagtype}) and (defined $properties{$tagtype}{$canon_tagid})) { + + $log->debug("display_tag - checking properties", + {tagtype => $tagtype, canon_tagid => $canon_tagid, field => $field}) + if $log->is_debug(); - foreach my $key ('property', 'description', 'abstract', 'url', 'date') { + foreach my $key ('property', 'description', 'abstract', 'url', 'date') { - my $suffix = "_" . $key; - if ($key eq 'property') { - $suffix = ''; - } + my $suffix = "_" . $key; + if ($key eq 'property') { + $suffix = ''; + } - if (defined $properties{$tagtype}{$canon_tagid}{$fieldid . $suffix . ":" . $lc}) { - $propertyid{$key} = $fieldid . $suffix . ":" . $lc; - $log->debug( - "display_tag - property key is defined for lc $lc", - { - tagtype => $tagtype, - canon_tagid => $canon_tagid, - field => $field, - key => $key, - propertyid => $propertyid{$key} - } - ) if $log->is_debug(); + if (defined $properties{$tagtype}{$canon_tagid}{$fieldid . $suffix . ":" . $lc}) { + $propertyid{$key} = $fieldid . $suffix . ":" . $lc; + $log->debug( + "display_tag - property key is defined for lc $lc", + { + tagtype => $tagtype, + canon_tagid => $canon_tagid, + field => $field, + key => $key, + propertyid => $propertyid{$key} } - elsif (defined $properties{$tagtype}{$canon_tagid}{$fieldid . $suffix . ":" . "en"}) { - $propertyid{$key} = $fieldid . $suffix . ":" . "en"; - $log->debug( - "display_tag - property key is defined for en", - { - tagtype => $tagtype, - canon_tagid => $canon_tagid, - field => $field, - key => $key, - propertyid => $propertyid{$key} - } - ) if $log->is_debug(); + ) if $log->is_debug(); + } + elsif (defined $properties{$tagtype}{$canon_tagid}{$fieldid . $suffix . ":" . "en"}) { + $propertyid{$key} = $fieldid . $suffix . ":" . "en"; + $log->debug( + "display_tag - property key is defined for en", + { + tagtype => $tagtype, + canon_tagid => $canon_tagid, + field => $field, + key => $key, + propertyid => $propertyid{$key} } - else { - $log->debug( - "display_tag - property key is not defined", - { - tagtype => $tagtype, - canon_tagid => $canon_tagid, - field => $field, - key => $key, - propertyid => $propertyid{$key} - } - ) if $log->is_debug(); + ) if $log->is_debug(); + } + else { + $log->debug( + "display_tag - property key is not defined", + { + tagtype => $tagtype, + canon_tagid => $canon_tagid, + field => $field, + key => $key, + propertyid => $propertyid{$key} } - } + ) if $log->is_debug(); } + } + } - $log->debug( - "display_tag", - { - tagtype => $tagtype, - canon_tagid => $canon_tagid, - field_orig => $field_orig, - field => $field, - propertyid => $propertyid{property}, - array => $array - } - ) if $log->is_debug(); + $log->debug( + "display_tag", + { + tagtype => $tagtype, + canon_tagid => $canon_tagid, + field_orig => $field_orig, + field => $field, + propertyid => $propertyid{property}, + array => $array + } + ) if $log->is_debug(); - if ((defined $propertyid{property}) or (defined $propertyid{abstract})) { + if ((defined $propertyid{property}) or (defined $propertyid{abstract})) { - # wikipedia abstract? + # wikipedia abstract? - if ((defined $propertyid{abstract}) and ($fieldid eq "wikipedia")) { + if ((defined $propertyid{abstract}) and ($fieldid eq "wikipedia")) { - my $site = $fieldid; + my $site = $fieldid; - $log->debug("display_tag - showing abstract", {site => $site}) if $log->is_debug(); + $log->debug("display_tag - showing abstract", {site => $site}) if $log->is_debug(); - $description .= "

" . $properties{$tagtype}{$canon_tagid}{$propertyid{abstract}}; + $description .= "

" . $properties{$tagtype}{$canon_tagid}{$propertyid{abstract}}; - if (defined $propertyid{url}) { + if (defined $propertyid{url}) { - my $lang_site = lang($site); - if ((defined $lang_site) and ($lang_site ne "")) { - $site = $lang_site; - } - $description - .= ' - ' - . $site . ''; - } + my $lang_site = lang($site); + if ((defined $lang_site) and ($lang_site ne "")) { + $site = $lang_site; + } + $description + .= ' - ' + . $site . ''; + } - $description .= "

"; + $description .= "

"; - next; - } + next; + } - my $title; - my $tagtype_field = $tagtype . '_' . $fieldid; - # $tagtype_field =~ s/_/-/g; - if (exists $Lang{$tagtype_field}{$lc}) { - $title = $Lang{$tagtype_field}{$lc}; - } - elsif (exists $Lang{$fieldid}{$lc}) { - $title = $Lang{$fieldid}{$lc}; - } + my $title; + my $tagtype_field = $tagtype . '_' . $fieldid; + # $tagtype_field =~ s/_/-/g; + if (exists $Lang{$tagtype_field}{$lc}) { + $title = $Lang{$tagtype_field}{$lc}; + } + elsif (exists $Lang{$fieldid}{$lc}) { + $title = $Lang{$fieldid}{$lc}; + } - $log->debug("display_tag - title", {tagtype => $tagtype, title => $title}) if $log->is_debug(); + $log->debug("display_tag - title", {tagtype => $tagtype, title => $title}) if $log->is_debug(); - $description .= "

"; + $description .= "

"; - if (defined $title) { - $description .= "" . $title . "" . separator_before_colon($lc) . ": "; - } + if (defined $title) { + $description .= "" . $title . "" . separator_before_colon($lc) . ": "; + } - my @values = ($properties{$tagtype}{$canon_tagid}{$propertyid{property}}); + my @values = ($properties{$tagtype}{$canon_tagid}{$propertyid{property}}); - if ($array) { - @values = split(/,/, $properties{$tagtype}{$canon_tagid}{$propertyid{property}}); - } + if ($array) { + @values = split(/,/, $properties{$tagtype}{$canon_tagid}{$propertyid{property}}); + } - my $values_display = ""; + my $values_display = ""; - foreach my $value_orig (@values) { + foreach my $value_orig (@values) { - my $value = $value_orig; # make a copy so that we can modify it inside the foreach loop + my $value = $value_orig; # make a copy so that we can modify it inside the foreach loop - next if $value =~ /^\s*$/; + next if $value =~ /^\s*$/; - $value =~ s/^\s+//; - $value =~ s/\s+$//; + $value =~ s/^\s+//; + $value =~ s/\s+$//; - my $property_tagtype = $fieldid; + my $property_tagtype = $fieldid; - $property_tagtype =~ s/-/_/g; + $property_tagtype =~ s/-/_/g; - if (not exists $taxonomy_fields{$property_tagtype}) { - # try with an additional s - $property_tagtype .= "s"; - } + if (not exists $taxonomy_fields{$property_tagtype}) { + # try with an additional s + $property_tagtype .= "s"; + } - $log->debug("display_tag", {property_tagtype => $property_tagtype, lc => $lc, value => $value}) - if $log->is_debug(); + $log->debug("display_tag", {property_tagtype => $property_tagtype, lc => $lc, value => $value}) + if $log->is_debug(); - my $display = $value; + my $display = $value; - if (exists $taxonomy_fields{$property_tagtype}) { + if (exists $taxonomy_fields{$property_tagtype}) { - $display = display_taxonomy_tag($lc, $property_tagtype, $value); + $display = display_taxonomy_tag($lc, $property_tagtype, $value); - $log->debug("display_tag - $property_tagtype is a taxonomy", {display => $display}) - if $log->is_debug(); + $log->debug("display_tag - $property_tagtype is a taxonomy", {display => $display}) + if $log->is_debug(); - if ( (defined $properties{$property_tagtype}) - and (defined $properties{$property_tagtype}{$value})) - { + if ( (defined $properties{$property_tagtype}) + and (defined $properties{$property_tagtype}{$value})) + { - # tooltip - - my $tooltip; - - if (defined $properties{$property_tagtype}{$value}{"description:$lc"}) { - $tooltip = $properties{$property_tagtype}{$value}{"description:$lc"}; - } - elsif (defined $properties{$property_tagtype}{$value}{"description:en"}) { - $tooltip = $properties{$property_tagtype}{$value}{"description:en"}; - } - - if (defined $tooltip) { - $display - = '' - . $display - . ''; - } - else { - $log->debug("display_tag - no tooltip", - {property_tagtype => $property_tagtype, value => $value}) - if $log->is_debug(); - } + # tooltip - } - else { - $log->debug("display_tag - no property found", - {property_tagtype => $property_tagtype, value => $value}) - if $log->is_debug(); - } + my $tooltip; + + if (defined $properties{$property_tagtype}{$value}{"description:$lc"}) { + $tooltip = $properties{$property_tagtype}{$value}{"description:$lc"}; + } + elsif (defined $properties{$property_tagtype}{$value}{"description:en"}) { + $tooltip = $properties{$property_tagtype}{$value}{"description:en"}; + } + + if (defined $tooltip) { + $display + = '' + . $display + . ''; } else { - $log->debug("display_tag - not a taxonomy", + $log->debug("display_tag - no tooltip", {property_tagtype => $property_tagtype, value => $value}) if $log->is_debug(); + } - # Do we have a translation for the field? + } + else { + $log->debug("display_tag - no property found", + {property_tagtype => $property_tagtype, value => $value}) + if $log->is_debug(); + } + } + else { + $log->debug("display_tag - not a taxonomy", + {property_tagtype => $property_tagtype, value => $value}) + if $log->is_debug(); - my $valueid = $value; - $valueid =~ s/^en://; + # Do we have a translation for the field? - # check if the value translate to a field specific value + my $valueid = $value; + $valueid =~ s/^en://; - if (exists $Lang{$tagtype_field . "_" . $valueid}{$lc}) { - $display = $Lang{$tagtype_field . "_" . $valueid}{$lc}; - } + # check if the value translate to a field specific value + + if (exists $Lang{$tagtype_field . "_" . $valueid}{$lc}) { + $display = $Lang{$tagtype_field . "_" . $valueid}{$lc}; + } - # check if we have an icon - if (exists $Lang{$tagtype_field . "_icon_alt_" . $valueid}{$lc}) { - my $alt = $Lang{$tagtype_field . "_icon_alt_" . $valueid}{$lc}; - my $iconid = $tagtype_field . "_icon_" . $valueid; - $iconid =~ s/_/-/g; - $display = <

$alt @@ -3673,95 +3516,210 @@ $display
HTML - ; - } + ; + } - # otherwise check if we have a general value + # otherwise check if we have a general value - elsif (exists $Lang{$valueid}{$lc}) { - $display = $Lang{$valueid}{$lc}; - } + elsif (exists $Lang{$valueid}{$lc}) { + $display = $Lang{$valueid}{$lc}; + } - $log->debug("display_tag - display value", {display => $display}) if $log->is_debug(); + $log->debug("display_tag - display value", {display => $display}) if $log->is_debug(); - # tooltip + # tooltip - if (exists $Lang{$valueid . "_description"}{$lc}) { + if (exists $Lang{$valueid . "_description"}{$lc}) { - my $tooltip = $Lang{$valueid . "_description"}{$lc}; + my $tooltip = $Lang{$valueid . "_description"}{$lc}; - $display - = '' - . $display - . ''; + $display + = '' + . $display + . ''; - } - else { - $log->debug("display_tag - no description", {valueid => $valueid}) if $log->is_debug(); - } + } + else { + $log->debug("display_tag - no description", {valueid => $valueid}) if $log->is_debug(); + } - # link + # link - if (exists $propertyid{url}) { - $display - = '' - . $display . ""; - } - if (exists $Lang{$valueid . "_url"}{$lc}) { - $display = '' . $display . ""; - } - else { - $log->debug("display_tag - no url", {valueid => $valueid}) if $log->is_debug(); - } + if (exists $propertyid{url}) { + $display + = '' + . $display . ""; + } + if (exists $Lang{$valueid . "_url"}{$lc}) { + $display = '' . $display . ""; + } + else { + $log->debug("display_tag - no url", {valueid => $valueid}) if $log->is_debug(); + } - # date + # date - if (exists $propertyid{date}) { - $display .= " (" . $properties{$tagtype}{$canon_tagid}{$propertyid{date}} . ")"; - } - if (exists $Lang{$valueid . "_date"}{$lc}) { - $display .= " (" . $Lang{$valueid . "_date"}{$lc} . ")"; - } - else { - $log->debug("display_tag - no date", {valueid => $valueid}) if $log->is_debug(); - } + if (exists $propertyid{date}) { + $display .= " (" . $properties{$tagtype}{$canon_tagid}{$propertyid{date}} . ")"; + } + if (exists $Lang{$valueid . "_date"}{$lc}) { + $display .= " (" . $Lang{$valueid . "_date"}{$lc} . ")"; + } + else { + $log->debug("display_tag - no date", {valueid => $valueid}) if $log->is_debug(); + } - # abstract - if (exists $propertyid{abstract}) { - $display - .= "
" - . $properties{$tagtype}{$canon_tagid}{$propertyid{abstract}} - . "
"; - } + # abstract + if (exists $propertyid{abstract}) { + $display + .= "
" + . $properties{$tagtype}{$canon_tagid}{$propertyid{abstract}} + . "
"; + } - } + } - $values_display .= $display . ", "; - } - $values_display =~ s/, $//; + $values_display .= $display . ", "; + } + $values_display =~ s/, $//; - $description .= $values_display . "

\n"; + $description .= $values_display . "

\n"; - # Display an optional description of the property + # Display an optional description of the property - if (exists $Lang{$tagtype_field . "_description"}{$lc}) { - $description .= "

" . $Lang{$tagtype_field . "_description"}{$lc} . "

"; - } + if (exists $Lang{$tagtype_field . "_description"}{$lc}) { + $description .= "

" . $Lang{$tagtype_field . "_description"}{$lc} . "

"; + } - } - else { - $log->debug("display_tag - property not defined", - {tagtype => $tagtype, property_id => $propertyid{property}, canon_tagid => $canon_tagid}) - if $log->is_debug(); - } + } + else { + $log->debug("display_tag - property not defined", + {tagtype => $tagtype, property_id => $propertyid{property}, canon_tagid => $canon_tagid}) + if $log->is_debug(); + } + } + + # Remove titles without content + + $description =~ s/

([^<]+)<\/h3>\s*(

)/

/isg; + $description =~ s/

([^<]+)<\/h3>\s*$//isg; + + return $description; +} + +=head2 display_tag ( $request_ref ) + +This function is called to display either: + +1. Products that have a specific tag: /category/cakes + or that don't have a specific tag /category/-cakes + or that have 2 specific tags /category/cake/brand/oreo +2. List of tags of a given type: /labels + possibly for products that have a specific tag: /category/cakes/labels + or more specific tags: /category/cakes/label/organic/additives + +When displaying products for a tag, the function generates tag type specific HTML +that is displayed at the top of the page: +- tag parents and children +- maps for tag types that have a location (e.g. packaging codes) +- special properties for some tag types (e.g. additives) + +The function then calls search_and_display_products() to display the paginated list of products. + +When displaying a list of tags, the function calls display_list_of_tags(). + +=cut + +sub display_tag ($request_ref) { + + local $log->context->{tags} = $request_ref->{tags}; + + init_tags_texts() unless %tags_texts; + + canonicalize_request_tags_and_redirect_to_canonical_url($request_ref); + + my $title = generate_title_from_request_tags($request_ref->{tags}); + + # Refactoring in progress + # TODO: some of the following variables may be removed, and instead we could use the $request_ref->{tags} array + my $tagtype = deep_get($request_ref, qw(tags 0 tagtype)); + my $tagid = deep_get($request_ref, qw(tags 0 tagid)); + my $display_tag = deep_get($request_ref, qw(tags 0 display_tag)); + my $new_tagid = deep_get($request_ref, qw(tags 0 new_tagid)); + my $new_tagid_path = deep_get($request_ref, qw(tags 0 new_tagid_path)); + my $canon_tagid = deep_get($request_ref, qw(tags 0 canon_tagid)); + + my $tagtype2 = deep_get($request_ref, qw(tags 1 tagtype)); + my $tagid2 = deep_get($request_ref, qw(tags 1 tagid)); + my $display_tag2 = deep_get($request_ref, qw(tags 1 display_tag)); + my $new_tagid2 = deep_get($request_ref, qw(tags 1 new_tagid)); + my $new_tagid2path = deep_get($request_ref, qw(tags 1 new_tagid_path)); + my $canon_tagid2 = deep_get($request_ref, qw(tags 1 canon_tagid)); + + my $weblinks_html = ''; + my @wikidata_objects = (); + if ( ($tagtype ne 'additives') + and (not defined $request_ref->{groupby_tagtype})) + { + my @weblinks = (); + if ((defined $properties{$tagtype}) and (defined $properties{$tagtype}{$canon_tagid})) { + foreach my $key (keys %weblink_templates) { + next if not defined $properties{$tagtype}{$canon_tagid}{$key}; + my $weblink = { + text => $weblink_templates{$key}{text}, + href => sprintf($weblink_templates{$key}{href}, $properties{$tagtype}{$canon_tagid}{$key}), + hreflang => $weblink_templates{$key}{hreflang}, + }; + $weblink->{title} = sprintf($weblink_templates{$key}{title}, $properties{$tagtype}{$canon_tagid}{$key}) + if defined $weblink_templates{$key}{title}; + push @weblinks, $weblink; } - # Remove titles without content + if ((defined $properties{$tagtype}) and (defined $properties{$tagtype}{$canon_tagid}{'wikidata:en'})) { + push @wikidata_objects, $properties{$tagtype}{$canon_tagid}{'wikidata:en'}; + } + } - $description =~ s/

([^<]+)<\/h3>\s*(

)/

/isg; - $description =~ s/

([^<]+)<\/h3>\s*$//isg; + if (($#weblinks >= 0)) { + $weblinks_html + .= ''; + } + } + + my $description = ''; + + my $icid = $tagid; + (defined $icid) and $icid =~ s/^.*://; + + # Gather data that will be passed to the tag template + my $tag_template_data_ref = {}; + + $tag_template_data_ref->{groupby_tagtype} = $request_ref->{groupby_tagtype}; + + if (defined $tagtype) { + + # check if there is a template to display additional fields from the taxonomy + # the template is set in the Config.pm file + # This feature was coded before the introduction of knowledge panels + # It is in maintenance mode, and should be reimplemented as facets knowledge panels + # (server side, or with client side facets knowledge panels) + + if (exists $options{"display_tag_" . $tagtype}) { + + $description = generate_description_from_display_tag_options($tagtype, $tagid, $display_tag, $canon_tagid); } else { # Do we have a description for the tag in the taxonomy? @@ -4005,7 +3963,6 @@ HTML my $app_user = f_lang("f_app_user", {app_name => $app_name}); $title = $app_user; - $products_title = $app_user; $display_tag = $app_user; } else { @@ -4023,7 +3980,6 @@ HTML if ($user_or_org_ref->{name} ne '') { $title = $user_or_org_ref->{name} || $tagid; - $products_title = $user_or_org_ref->{name}; $display_tag = $user_or_org_ref->{name}; } @@ -4058,15 +4014,15 @@ HTML $user_template_data_ref->{links} = [ { - text => sprintf(lang('contributors_products'), $products_title), + text => sprintf(lang('contributors_products'), $user_or_org_ref->{name}), url => canonicalize_tag_link("users", get_string_id_for_lang("no_language", $tagid)), }, { - text => sprintf(lang('editors_products'), $products_title), + text => sprintf(lang('editors_products'), $user_or_org_ref->{name}), url => canonicalize_tag_link("editors", get_string_id_for_lang("no_language", $tagid)), }, { - text => sprintf(lang('photographers_products'), $products_title), + text => sprintf(lang('photographers_products'), $user_or_org_ref->{name}), url => canonicalize_tag_link("photographers", get_string_id_for_lang("no_language", $tagid)), }, @@ -4117,55 +4073,15 @@ HTML } } - if ((defined $request_ref->{tag_prefix}) and ($request_ref->{tag_prefix} eq '-')) { - $products_title = sprintf(lang($tagtype . '_without_products'), $products_title); - } - else { - $products_title = sprintf(lang($tagtype . '_products'), $products_title); - } + # Pass template data to generate navigation links + # These are variables that ae used to inject data + # Used in tag.tt.html - if (defined $tagid2) { - $products_title .= lang("title_separator"); - if ((defined $request_ref->{tag2_prefix}) and ($request_ref->{tag2_prefix} eq '-')) { - $products_title .= sprintf(lang($tagtype2 . '_without_products'), $display_tag2); - } - else { - $products_title .= sprintf(lang($tagtype2 . '_products'), $display_tag2); - } - } + $tag_template_data_ref->{tags} = $request_ref->{tags}; if (not defined $request_ref->{groupby_tagtype}) { - # Pass template data to generate navigation links - # These are variables that ae used to inject data - # Used in tag.tt.html - #------------------------------------------------------- - # Results of these variables based for category/en:snacks - #---- tagtype would return-> categories ----- - #---- tagtype_path would return-> /categories ----- - #---- tagtype_name would return-> category ----- - #---- tagid would return-> en:snacks ----- - #---- tagid_path would return-> /category/snacks ----- - #---- tag_name would return-> Snacks ----- - - $tag_template_data_ref->{tagtype} = $tagtype; - $tag_template_data_ref->{tagtype_path} = '/' . $tag_type_plural{$tagtype}{$lc}; - $tag_template_data_ref->{tagtype_name} = lang($tagtype . '_s'); - $tag_template_data_ref->{tagid} = $tagid; - $tag_template_data_ref->{tagid_path} = $newtagidpath; - $tag_template_data_ref->{tag_name} = $display_tag; - $tag_template_data_ref->{canon_tagid} = $canon_tagid // $tagid; - - if (defined $tagid2) { - $tag_template_data_ref->{tagtype2} = $tagtype2; - $tag_template_data_ref->{tagtype2_path} = '/' . $tag_type_plural{$tagtype2}{$lc}; - $tag_template_data_ref->{tagtype2_name} = lang($tagtype2 . '_s'); - $tag_template_data_ref->{tagid2} = $tagid2; - $tag_template_data_ref->{tagid2_path} = $newtagid2path; - $tag_template_data_ref->{tag2_name} = $display_tag2; - $tag_template_data_ref->{canon_tagid2} = $canon_tagid2 // $tagid2; - } - else { + if (not defined $tagid2) { # We are on the main page of the tag (not a sub-page with another tag) # so we display more information related to the tag @@ -4202,10 +4118,7 @@ HTML = display_knowledge_panel($tag_ref, $tag_ref->{"knowledge_panels_" . $lc}, "root"); } } - - $tag_template_data_ref->{products_title} = $products_title; } - } # end of if (defined $tagtype) $tag_template_data_ref->{country} = $country; @@ -4227,95 +4140,26 @@ HTML } - my $query_ref = {}; - my $sort_by; - if ($tagtype eq 'users') { - $query_ref->{creator} = $tagid; - $sort_by = 'last_modified_t'; - } - elsif (defined $canon_tagid) { - if ((defined $request_ref->{tag_prefix}) and ($request_ref->{tag_prefix} ne '')) { - $query_ref->{($tagtype . "_tags")} = {"\$ne" => $canon_tagid}; - } - else { - $query_ref->{($tagtype . "_tags")} = $canon_tagid; - } - $sort_by = 'last_modified_t'; - } - elsif (defined $tagid) { - if ((defined $request_ref->{tag_prefix}) and ($request_ref->{tag_prefix} ne '')) { - $query_ref->{($tagtype . "_tags")} = {"\$ne" => $tagid}; + # Add parameters corresponding to the tag filters so that they can be added to the query by add_params_to_query() + + foreach my $tag_ref (@{$request_ref->{tags}}) { + if ($tagtype eq 'users') { + param('creator', $tagid); } else { - $query_ref->{($tagtype . "_tags")} = $tagid; + my $field_name = $tag_ref->{tagtype} . "_tags"; + my $current_value = param($field_name); + my $new_value = ($tag_ref->{tag_prefix} || '') . ($tag_ref->{canon_tagid} || $tag_ref->{tagid}); + if ($current_value) { + $new_value = $current_value . ',' . $new_value; + } + param($field_name, $new_value); } - $sort_by = 'last_modified_t'; } - # db.myCol.find({ mylist: { $ne: 'orange' } }) - - # unknown / empty value - # warning: unknown is a value for pnns_groups_1 and 2 - if ( - ( - ($tagid eq get_string_id_for_lang($lc, lang("unknown"))) - or ($tagid eq ($lc . ":" . get_string_id_for_lang($lc, lang("unknown")))) - ) - and ($tagtype !~ /^pnns_groups_/) - ) - { - #$query_ref = { ($tagtype . "_tags") => "[]"}; - $query_ref = {"\$or" => [{($tagtype) => undef}, {$tagtype => ""}]}; - } - - if (defined $tagid2) { - - my $field = $tagtype2 . "_tags"; - my $value = $tagid2; - $sort_by = 'last_modified_t'; - - if ($tagtype2 eq 'users') { - $field = "creator"; - } - - if (defined $canon_tagid2) { - $value = $canon_tagid2; - } - - my $tag2_is_negative = (defined $request_ref->{tag2_prefix} and $request_ref->{tag2_prefix} eq '-') ? 1 : 0; - - $log->debug("tag2_is_negative " . $tag2_is_negative) if $log->is_debug(); - # 2 criteria on the same field? - # we need to use the $and MongoDB syntax + my $query_ref = {}; + my $sort_by; - if (defined $query_ref->{$field}) { - my $and = [{$field => $query_ref->{$field}}]; - # fix for issue #2657: negative query on tag2 was not being honored if both tag types are the same - if ($tag2_is_negative) { - push @{$and}, {$field => {"\$ne" => $value}}; - } - else { - push @{$and}, {$field => $value}; - } - delete $query_ref->{$field}; - $query_ref->{"\$and"} = $and; - } - # unknown / empty value - elsif ( - ( - ($tagid2 eq get_string_id_for_lang($lc, lang("unknown"))) - or ($tagid2 eq ($lc . ":" . get_string_id_for_lang($lc, lang("unknown")))) - ) - and ($tagtype2 !~ /^pnns_groups_/) - ) - { - $query_ref->{"\$or"} = [{($tagtype2) => undef}, {$tagtype2 => ""}]; - } - else { - # issue 2285: second tag was not supporting the 'minus' query - $query_ref->{$field} = $tag2_is_negative ? {"\$ne" => $value} : $value; - } - } # Rendering Page tags my $tag_html; # TODO: is_crawl_bot should be added directly by process_template(), @@ -4331,9 +4175,6 @@ HTML else { ${$request_ref->{content_ref}} .= $tag_html . display_list_of_tags($request_ref, $query_ref); } - if ($products_title ne '') { - $request_ref->{title} .= " " . lang("for") . " " . lcfirst($products_title); - } $request_ref->{title} .= lang("title_separator") . display_taxonomy_tag($lc, "countries", $country); $request_ref->{page_type} = "list_of_tags"; } @@ -4786,11 +4627,37 @@ sub add_params_to_query ($request_ref, $query_ref) { {field => $field, lc => $lc, tag_lc => $tag_lc, tag => $tag, tagid => $tagid}) if $log->is_debug(); - if ($not) { - $query_ref->{$tagtype . $suffix} = {'$ne' => $tagid}; + # if the value is "unknown", we need to add a condition on the field being empty + # warning: unknown is a value for pnns_groups_1 and 2 + if ( + ( + ($tagid eq get_string_id_for_lang($tag_lc, lang("unknown"))) + or ( + $tagid eq ( + $tag_lc . ":" + . get_string_id_for_lang($tag_lc, lang_in_other_lc($tag_lc, "unknown")) + ) + ) + ) + and ($tagtype !~ /^pnns_groups_/) + ) + { + if ($not) { + $query_ref->{$tagtype . $suffix} = {'$nin' => [undef, []]}; + } + else { + $query_ref->{$tagtype . $suffix} = {'$in' => [undef, []]}; + } + } + # Normal single value (not unknown) else { - $query_ref->{$tagtype . $suffix} = $tagid; + if ($not) { + $query_ref->{$tagtype . $suffix} = {'$ne' => $tagid}; + } + else { + $query_ref->{$tagtype . $suffix} = $tagid; + } } } diff --git a/lib/ProductOpener/Routing.pm b/lib/ProductOpener/Routing.pm index 851c99ae826c1..fd7880bbc302a 100644 --- a/lib/ProductOpener/Routing.pm +++ b/lib/ProductOpener/Routing.pm @@ -56,6 +56,104 @@ use CGI qw/:cgi :form escapeHTML/; use URI::Escape::XS; use Log::Any qw($log); +=head2 sub extract_tagtype_and_tag_value_pairs_from_components($request_ref, $components_ref) + +Extract tag type / tag value pairs and store them in an array $request_ref->{tags} + +e.g. /category/breakfast-cereals/label/organic/brand/monoprix + +Tags can be prefixed by a - to indicate that we want products without this tag + +=cut + +sub extract_tagtype_and_tag_value_pairs_from_components ($request_ref, $components_ref) { + + $request_ref->{tags} = []; + + while ( + (scalar @$components_ref >= 2) + and ( (defined $tag_type_from_singular{$lc}{$components_ref->[0]}) + or (defined $tag_type_from_singular{"en"}{$components_ref->[0]})) + ) + { + my $tagtype; + my $tag_prefix; + my $tag; + my $tagid; + + $log->debug("request looks like a singular tag", + {lc => $lc, tagtype => $components_ref->[0], tagid => $components_ref->[1]}) + if $log->is_debug(); + + # If the first component is a valid singular tag type, use it as the tag type + if (defined $tag_type_from_singular{$lc}{$components_ref->[0]}) { + $tagtype = $tag_type_from_singular{$lc}{shift @$components_ref}; + } + # Otherwise, use "en" as the default language and try again + else { + $tagtype = $tag_type_from_singular{"en"}{shift @$components_ref}; + } + + $tag = shift @$components_ref; + + # if there is a leading dash - before the tag, it indicates we want products without it + if ($tag =~ /^-/) { + $tag_prefix = "-"; + $tag = $'; + } + else { + $tag_prefix = ""; + } + # If the tag type is a valid taxonomy field, try to canonicalize the tag ID + if (defined $taxonomy_fields{$tagtype}) { + my $parsed_tag = canonicalize_taxonomy_tag_linkeddata($tagtype, $tag); + if (not $parsed_tag) { + $parsed_tag = canonicalize_taxonomy_tag_weblink($tagtype, $tag); + } + + if ($parsed_tag) { + $tagid = $parsed_tag; + } + else { + if ($tag !~ /^(\w\w):/) { + $tag = $lc . ":" . $tag; + } + + $tagid = get_taxonomyid($lc, $tag); + } + } + else { + # Use "no_language" normalization + $tagid = get_string_id_for_lang("no_language", $tag); + } + + $request_ref->{canon_rel_url} + .= "/" . $tag_type_singular{$tagtype}{$lc} . "/" . $tag_prefix . $tagid; + + # Add the tag properties to the list of tags + push @{$request_ref->{tags}}, {tagtype => $tagtype, tag => $tagid, tagid => $tagid, tag_prefix => $tag_prefix}; + + # Temporarily store the tag properties in %request_ref keys tag, tagid, tagtype, tag_prefix and tag2 etc. + # to remain compatible with the rest of the code + # TODO: remove this once the rest of the code has been updated + + if (scalar keys @{$request_ref->{tags}} == 1) { + $request_ref->{tag} = $tagid; + $request_ref->{tagid} = $tagid; + $request_ref->{tagtype} = $tagtype; + $request_ref->{tag_prefix} = $tag_prefix; + } + elsif (scalar keys @{$request_ref->{tags}} == 2) { + $request_ref->{tag2} = $tagid; + $request_ref->{tagid2} = $tagid; + $request_ref->{tagtype2} = $tagtype; + $request_ref->{tag2_prefix} = $tag_prefix; + } + } + + return; +} + =head2 analyze_request ( $request_ref ) Analyze request parameters and decide which method to call. @@ -408,152 +506,31 @@ sub analyze_request ($request_ref) { if $log->is_debug(); } - if ( - ($#components >= 0) - and ( (defined $tag_type_from_singular{$lc}{$components[0]}) - or (defined $tag_type_from_singular{"en"}{$components[0]})) - ) - { - - $log->debug("request looks like a singular tag", {lc => $lc, tagid => $components[0]}) if $log->is_debug(); + # Extract tag type / tag value pairs and store them in an array $request_ref->{tags} + # e.g. /category/breakfast-cereals/label/organic/brand/monoprix + extract_tagtype_and_tag_value_pairs_from_components($request_ref, \@components); - # If the first component is a valid singular tag type, use it as the tag type - if (defined $tag_type_from_singular{$lc}{$components[0]}) { - $request_ref->{tagtype} = $tag_type_from_singular{$lc}{shift @components}; - } - # Otherwise, use "en" as the default language and try again - else { - $request_ref->{tagtype} = $tag_type_from_singular{"en"}{shift @components}; - } - - my $tagtype = $request_ref->{tagtype}; - - if (($#components >= 0)) { - $request_ref->{tag} = shift @components; - - # if there is a leading dash - before the tag, it indicates we want products without it - if ($request_ref->{tag} =~ /^-/) { - $request_ref->{tag_prefix} = "-"; - $request_ref->{tag} = $'; - } - else { - $request_ref->{tag_prefix} = ""; - } - # If the tag type is a valid taxonomy field, try to canonicalize the tag ID - if (defined $taxonomy_fields{$tagtype}) { - my $parsed_tag = canonicalize_taxonomy_tag_linkeddata($tagtype, $request_ref->{tag}); - if (not $parsed_tag) { - $parsed_tag = canonicalize_taxonomy_tag_weblink($tagtype, $request_ref->{tag}); - } - - if ($parsed_tag) { - $request_ref->{tagid} = $parsed_tag; - } - else { - if ($request_ref->{tag} !~ /^(\w\w):/) { - $request_ref->{tag} = $lc . ":" . $request_ref->{tag}; - } - - $request_ref->{tagid} = get_taxonomyid($lc, $request_ref->{tag}); - } - } - else { - # Use "no_language" normalization - $request_ref->{tagid} = get_string_id_for_lang("no_language", $request_ref->{tag}); - } - } - - $request_ref->{canon_rel_url} - .= "/" . $tag_type_singular{$tagtype}{$lc} . "/" . $request_ref->{tag_prefix} . $request_ref->{tagid}; - - # 2nd tag? - - if ( - ($#components >= 0) - and ( (defined $tag_type_from_singular{$lc}{$components[0]}) - or (defined $tag_type_from_singular{"en"}{$components[0]})) - ) - { - - if (defined $tag_type_from_singular{$lc}{$components[0]}) { - $request_ref->{tagtype2} = $tag_type_from_singular{$lc}{shift @components}; - } - else { - $request_ref->{tagtype2} = $tag_type_from_singular{"en"}{shift @components}; - } - my $tagtype = $request_ref->{tagtype2}; - - if (($#components >= 0)) { - $request_ref->{tag2} = shift @components; - - # if there is a leading dash - before the tag, it indicates we want products without it - if ($request_ref->{tag2} =~ /^-/) { - $request_ref->{tag2_prefix} = "-"; - $request_ref->{tag2} = $'; - } - else { - $request_ref->{tag2_prefix} = ""; - } - - if (defined $taxonomy_fields{$tagtype}) { - my $parsed_tag2 = canonicalize_taxonomy_tag_linkeddata($tagtype, $request_ref->{tag2}); - if (not $parsed_tag2) { - $parsed_tag2 = canonicalize_taxonomy_tag_weblink($tagtype, $request_ref->{tag2}); - } - - if ($parsed_tag2) { - $request_ref->{tagid2} = $parsed_tag2; - } - else { - if ($request_ref->{tag2} !~ /^(\w\w):/) { - $request_ref->{tag2} = $lc . ":" . $request_ref->{tag2}; - } - - $request_ref->{tagid2} = get_taxonomyid($lc, $request_ref->{tag2}); - } - } - else { - # Use "no_language" normalization - $request_ref->{tagid2} = get_string_id_for_lang("no_language", $request_ref->{tag2}); - } - } - - $request_ref->{canon_rel_url} - .= "/" - . $tag_type_singular{$tagtype}{$lc} . "/" - . $request_ref->{tag2_prefix} - . $request_ref->{tagid2}; - } - - if ((defined $components[0]) and ($components[0] eq 'points')) { - $request_ref->{points} = 1; - $request_ref->{canon_rel_url} .= "/points"; - } - - } - elsif ((defined $components[0]) and ($components[0] eq 'points')) { + # Old Open Food Hunt points + if ((defined $components[0]) and ($components[0] eq 'points')) { $request_ref->{points} = 1; $request_ref->{canon_rel_url} .= "/points"; } - elsif (not defined $request_ref->{groupby_tagtype}) { - $log->warn("invalid address, confused by number of components left", {left_components => $#components}) - if $log->is_warn(); - $request_ref->{status_code} = 404; - $request_ref->{error_message} = lang("error_invalid_address"); - } - # We have a component left + # We may have a page number if ($#components >= 0) { # The last component can be a page number - if ($components[-1] =~ /^\d+$/) { + if (($components[-1] =~ /^\d+$/) and ($components[-1] <= 1000)) { $request_ref->{page} = pop @components; } - else { - # We have a component left, but we don't know what it is - $request_ref->{status_code} = 404; - $request_ref->{error_message} = lang("error_invalid_address"); - return; - } + } + + if ($#components >= 0) { + # We have a component left, but we don't know what it is + $log->warn("invalid address, confused by number of components left", {left_components => \@components}) + if $log->is_warn(); + $request_ref->{status_code} = 404; + $request_ref->{error_message} = lang("error_invalid_address"); + return; } $request_ref->{canon_rel_url} .= $canon_rel_url_suffix; diff --git a/lib/ProductOpener/Tags.pm b/lib/ProductOpener/Tags.pm index a8391f39e58c1..1ffa0c244e142 100644 --- a/lib/ProductOpener/Tags.pm +++ b/lib/ProductOpener/Tags.pm @@ -2853,15 +2853,30 @@ sub display_tag_link ($tagtype, $tag) { return $html; } -sub canonicalize_taxonomy_tag_link ($target_lc, $tagtype, $tag) { +=head2 canonicalize_taxonomy_tag_link ($target_lc, $tagtype, $tag, $tag_prefix = undef) + +Returns a link to the canonicalized tag + +=head3 Arguments + +=head4 $tagtype + +=head4 $tagid + +=head4 $tag_prefix (optional) + +Can be - to indicate that the tag is a negative tag + +=cut + +sub canonicalize_taxonomy_tag_link ($target_lc, $tagtype, $tag, $tag_prefix = undef) { $target_lc =~ s/_.*//; $tag = display_taxonomy_tag($target_lc, $tagtype, $tag); my $tagurl = get_taxonomyurl($target_lc, $tag); my $path = $tag_type_singular{$tagtype}{$target_lc}; - $log->info("tax tag 1 /$path/$tagurl") if $log->is_info(); - return "/$path/$tagurl"; + return "/$path/" . ($tag_prefix // '') . "$tagurl"; } # The display_taxonomy_tag_link function makes many calls to other functions, in particular it calls twice display_taxonomy_tag_link @@ -4181,7 +4196,23 @@ sub display_taxonomy_tag_name ($target_lc, $tagtype, $canon_tagid) { return $display_value; } -sub canonicalize_tag_link ($tagtype, $tagid) { +=head2 canonicalize_tag_link ($tagtype, $tagid, $tag_prefix = undef) + +Return a relative link to a tag page. + +=head3 Arguments + +=head4 $tagtype + +=head4 $tagid + +=head4 $tag_prefix (optional) + +Can be - to indicate that the tag is a negative tag + +=cut + +sub canonicalize_tag_link ($tagtype, $tagid, $tag_prefix = undef) { if (defined $taxonomy_fields{$tagtype}) { die "ERROR: canonicalize_tag_link called for a taxonomy tagtype: $tagtype - tagid: $tagid - $!"; @@ -4198,7 +4229,7 @@ sub canonicalize_tag_link ($tagtype, $tagid) { $path = $tag_type_singular{$tagtype}{en}; } - my $link = "/$path/" . URI::Escape::XS::encodeURIComponent($tagid); + my $link = "/$path/" . ($tag_prefix // '') . URI::Escape::XS::encodeURIComponent($tagid); $log->info("canonicalize_tag_link $tagtype $tagid $path $link") if $log->is_info(); diff --git a/lib/ProductOpener/Test.pm b/lib/ProductOpener/Test.pm index 875412e736395..f7937443f247a 100644 --- a/lib/ProductOpener/Test.pm +++ b/lib/ProductOpener/Test.pm @@ -46,6 +46,7 @@ BEGIN { &normalize_org_for_test_comparison &normalize_product_for_test_comparison &normalize_products_for_test_comparison + &sort_products_for_test_comparison &normalize_user_for_test_comparison &remove_all_products &remove_all_users @@ -743,6 +744,24 @@ sub normalize_products_for_test_comparison ($array_ref) { return; } +=head2 sort_products_for_test_comparison($array_ref, $sort_field) + +Sort products so that they are always in the same order + +=head3 Arguments + +=head4 array_ref + +Array of products + +=cut + +sub sort_products_for_test_comparison ($array_ref, $sort_field) { + + @$array_ref = sort {$a->{$sort_field} cmp $b->{$sort_field}} @$array_ref; + return; +} + =head2 normalize_user_for_test_comparison($user_ref) Normalize a user to be able to compare them across tests runs. diff --git a/lib/ProductOpener/TestDefaults.pm b/lib/ProductOpener/TestDefaults.pm index d105a97d415fe..df19274b690f1 100644 --- a/lib/ProductOpener/TestDefaults.pm +++ b/lib/ProductOpener/TestDefaults.pm @@ -33,6 +33,7 @@ BEGIN { %default_org_edit_admin_form %default_product %default_product_form + %empty_product_form %default_user_form %moderator_user_form %pro_moderator_user_form @@ -115,13 +116,14 @@ NB: must be created by an admin serving_size => "10 g", ); -%default_product_form = ( - %default_product, +%empty_product_form = ( action => "process", type => "add", ".submit" => "submit" ); +%default_product_form = (%default_product, %empty_product_form,); + %default_org_edit_form = ( orgid => "acme-inc", action => "process", diff --git a/stop_words.txt b/stop_words.txt index 2dac8524223a8..eb226cc3f399c 100644 --- a/stop_words.txt +++ b/stop_words.txt @@ -31,6 +31,7 @@ boolean Brassicas canneberge canonicalize +canonicalizes canonicalized Canonicalizes Carrefour @@ -192,6 +193,7 @@ prepended Prunus publique Pulpe +reimplemented rescale Robotoff RTFSG diff --git a/templates/web/pages/tag/tag.tt.html b/templates/web/pages/tag/tag.tt.html index 260e7e3fc7fa5..59e50ba8485e9 100644 --- a/templates/web/pages/tag/tag.tt.html +++ b/templates/web/pages/tag/tag.tt.html @@ -2,19 +2,16 @@
+ +
+ [% FOREACH tag IN tags %] + [% tag.tagtype_name.ucfirst %][% sep %]: + [% tag.tag_prefix %][% tag.display_tag.ucfirst %] + [% IF loop.last %][% ELSE %] / [% END %] + [% END %] +
+ [% IF not groupby_tagtype.defined %] -
- [% tagtype_name.ucfirst %] - [% sep %]: - [% tag_name %] - - [% IF tagid2.defined %] -
- [% tagtype2_name.ucfirst %] - [% sep %]: - [% tag2_name %] - [% END %] -
[% IF tag_logo %] [% END %] - [% IF weblinks and not facets_kp_url.defined %] - -
- [% IF tagid.defined %] - [% IF facets_kp_url.defined && ! is_crawl_bot %] - -
-

-
-
- - - [% END %] +
[% END %] -
+ [% END %]
diff --git a/tests/integration/expected_test_results/facets/brand_-brand1.json b/tests/integration/expected_test_results/facets/brand_-brand1.json new file mode 100644 index 0000000000000..ec6fc8d3e5a67 --- /dev/null +++ b/tests/integration/expected_test_results/facets/brand_-brand1.json @@ -0,0 +1,30 @@ +{ + "count" : 7, + "page" : 1, + "page_count" : 7, + "page_size" : 24, + "products" : [ + { + "product_name" : "Apples - Organic - France" + }, + { + "product_name" : "Apples - Organic - France, Belgium, Canada" + }, + { + "product_name" : "Apples - Organic, Fair trade - France" + }, + { + "product_name" : "Bananas - Organic - France - brand2" + }, + { + "product_name" : "Carrots - No label - Belgium - brand2" + }, + { + "product_name" : "Chocolate - Organic, Fair trade - Martinique" + }, + { + "product_name" : "Oranges - Fair trade - Italy - brand3" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/brand_-unknown.json b/tests/integration/expected_test_results/facets/brand_-unknown.json new file mode 100644 index 0000000000000..048a415d586d0 --- /dev/null +++ b/tests/integration/expected_test_results/facets/brand_-unknown.json @@ -0,0 +1,33 @@ +{ + "count" : 8, + "page" : 1, + "page_count" : 8, + "page_size" : 24, + "products" : [ + { + "product_name" : "Bananas - Organic - France - brand2" + }, + { + "product_name" : "Bananas - Organic - Martinique - brand1" + }, + { + "product_name" : "Carrots - Fair trade - Italy - brand1, brand2" + }, + { + "product_name" : "Carrots - Fair trade, Organic - France - brand1" + }, + { + "product_name" : "Carrots - No label - Belgium - brand2" + }, + { + "product_name" : "Carrots - Organic - France - brand1, brand2" + }, + { + "product_name" : "Oranges - Fair trade - Italy - brand3" + }, + { + "product_name" : "Oranges - Organic - Spain - brand1, brand2, brand3" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/brand_brand1.json b/tests/integration/expected_test_results/facets/brand_brand1.json new file mode 100644 index 0000000000000..cab6350bdbcfb --- /dev/null +++ b/tests/integration/expected_test_results/facets/brand_brand1.json @@ -0,0 +1,24 @@ +{ + "count" : 5, + "page" : 1, + "page_count" : 5, + "page_size" : 24, + "products" : [ + { + "product_name" : "Bananas - Organic - Martinique - brand1" + }, + { + "product_name" : "Carrots - Fair trade - Italy - brand1, brand2" + }, + { + "product_name" : "Carrots - Fair trade, Organic - France - brand1" + }, + { + "product_name" : "Carrots - Organic - France - brand1, brand2" + }, + { + "product_name" : "Oranges - Organic - Spain - brand1, brand2, brand3" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/brand_brand2_brand_-brand1.json b/tests/integration/expected_test_results/facets/brand_brand2_brand_-brand1.json new file mode 100644 index 0000000000000..ee8b1a0bf9c45 --- /dev/null +++ b/tests/integration/expected_test_results/facets/brand_brand2_brand_-brand1.json @@ -0,0 +1,15 @@ +{ + "count" : 2, + "page" : 1, + "page_count" : 2, + "page_size" : 24, + "products" : [ + { + "product_name" : "Bananas - Organic - France - brand2" + }, + { + "product_name" : "Carrots - No label - Belgium - brand2" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/brand_unknown.json b/tests/integration/expected_test_results/facets/brand_unknown.json new file mode 100644 index 0000000000000..07471b66408df --- /dev/null +++ b/tests/integration/expected_test_results/facets/brand_unknown.json @@ -0,0 +1,21 @@ +{ + "count" : 4, + "page" : 1, + "page_count" : 4, + "page_size" : 24, + "products" : [ + { + "product_name" : "Apples - Organic - France" + }, + { + "product_name" : "Apples - Organic - France, Belgium, Canada" + }, + { + "product_name" : "Apples - Organic, Fair trade - France" + }, + { + "product_name" : "Chocolate - Organic, Fair trade - Martinique" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/category_-apples.json b/tests/integration/expected_test_results/facets/category_-apples.json new file mode 100644 index 0000000000000..2a872feffcf86 --- /dev/null +++ b/tests/integration/expected_test_results/facets/category_-apples.json @@ -0,0 +1,36 @@ +{ + "count" : 9, + "page" : 1, + "page_count" : 9, + "page_size" : 24, + "products" : [ + { + "product_name" : "Bananas - Organic - France - brand2" + }, + { + "product_name" : "Bananas - Organic - Martinique - brand1" + }, + { + "product_name" : "Carrots - Fair trade - Italy - brand1, brand2" + }, + { + "product_name" : "Carrots - Fair trade, Organic - France - brand1" + }, + { + "product_name" : "Carrots - No label - Belgium - brand2" + }, + { + "product_name" : "Carrots - Organic - France - brand1, brand2" + }, + { + "product_name" : "Chocolate - Organic, Fair trade - Martinique" + }, + { + "product_name" : "Oranges - Fair trade - Italy - brand3" + }, + { + "product_name" : "Oranges - Organic - Spain - brand1, brand2, brand3" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/category_-apples_label_-organic.json b/tests/integration/expected_test_results/facets/category_-apples_label_-organic.json new file mode 100644 index 0000000000000..5ed4e733aec59 --- /dev/null +++ b/tests/integration/expected_test_results/facets/category_-apples_label_-organic.json @@ -0,0 +1,18 @@ +{ + "count" : 3, + "page" : 1, + "page_count" : 3, + "page_size" : 24, + "products" : [ + { + "product_name" : "Carrots - Fair trade - Italy - brand1, brand2" + }, + { + "product_name" : "Carrots - No label - Belgium - brand2" + }, + { + "product_name" : "Oranges - Fair trade - Italy - brand3" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/category_-apples_label_organic.json b/tests/integration/expected_test_results/facets/category_-apples_label_organic.json new file mode 100644 index 0000000000000..b082e074d23b2 --- /dev/null +++ b/tests/integration/expected_test_results/facets/category_-apples_label_organic.json @@ -0,0 +1,27 @@ +{ + "count" : 6, + "page" : 1, + "page_count" : 6, + "page_size" : 24, + "products" : [ + { + "product_name" : "Bananas - Organic - France - brand2" + }, + { + "product_name" : "Bananas - Organic - Martinique - brand1" + }, + { + "product_name" : "Carrots - Fair trade, Organic - France - brand1" + }, + { + "product_name" : "Carrots - Organic - France - brand1, brand2" + }, + { + "product_name" : "Chocolate - Organic, Fair trade - Martinique" + }, + { + "product_name" : "Oranges - Organic - Spain - brand1, brand2, brand3" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/category_apples.json b/tests/integration/expected_test_results/facets/category_apples.json new file mode 100644 index 0000000000000..137d8fc8c2c44 --- /dev/null +++ b/tests/integration/expected_test_results/facets/category_apples.json @@ -0,0 +1,18 @@ +{ + "count" : 3, + "page" : 1, + "page_count" : 3, + "page_size" : 24, + "products" : [ + { + "product_name" : "Apples - Organic - France" + }, + { + "product_name" : "Apples - Organic - France, Belgium, Canada" + }, + { + "product_name" : "Apples - Organic, Fair trade - France" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/category_apples_label_organic.json b/tests/integration/expected_test_results/facets/category_apples_label_organic.json new file mode 100644 index 0000000000000..137d8fc8c2c44 --- /dev/null +++ b/tests/integration/expected_test_results/facets/category_apples_label_organic.json @@ -0,0 +1,18 @@ +{ + "count" : 3, + "page" : 1, + "page_count" : 3, + "page_size" : 24, + "products" : [ + { + "product_name" : "Apples - Organic - France" + }, + { + "product_name" : "Apples - Organic - France, Belgium, Canada" + }, + { + "product_name" : "Apples - Organic, Fair trade - France" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/category_bananas_label_organic_brand_-brand1.json b/tests/integration/expected_test_results/facets/category_bananas_label_organic_brand_-brand1.json new file mode 100644 index 0000000000000..9839208ca4676 --- /dev/null +++ b/tests/integration/expected_test_results/facets/category_bananas_label_organic_brand_-brand1.json @@ -0,0 +1,12 @@ +{ + "count" : 1, + "page" : 1, + "page_count" : 1, + "page_size" : 24, + "products" : [ + { + "product_name" : "Bananas - Organic - France - brand2" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/category_bananas_label_organic_brand_brand1.json b/tests/integration/expected_test_results/facets/category_bananas_label_organic_brand_brand1.json new file mode 100644 index 0000000000000..b6129feab756d --- /dev/null +++ b/tests/integration/expected_test_results/facets/category_bananas_label_organic_brand_brand1.json @@ -0,0 +1,12 @@ +{ + "count" : 1, + "page" : 1, + "page_count" : 1, + "page_size" : 24, + "products" : [ + { + "product_name" : "Bananas - Organic - Martinique - brand1" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/contributor_tests.json b/tests/integration/expected_test_results/facets/contributor_tests.json new file mode 100644 index 0000000000000..5550899dc9fed --- /dev/null +++ b/tests/integration/expected_test_results/facets/contributor_tests.json @@ -0,0 +1,45 @@ +{ + "count" : 12, + "page" : 1, + "page_count" : 12, + "page_size" : 24, + "products" : [ + { + "product_name" : "Apples - Organic - France" + }, + { + "product_name" : "Apples - Organic - France, Belgium, Canada" + }, + { + "product_name" : "Apples - Organic, Fair trade - France" + }, + { + "product_name" : "Bananas - Organic - France - brand2" + }, + { + "product_name" : "Bananas - Organic - Martinique - brand1" + }, + { + "product_name" : "Carrots - Fair trade - Italy - brand1, brand2" + }, + { + "product_name" : "Carrots - Fair trade, Organic - France - brand1" + }, + { + "product_name" : "Carrots - No label - Belgium - brand2" + }, + { + "product_name" : "Carrots - Organic - France - brand1, brand2" + }, + { + "product_name" : "Chocolate - Organic, Fair trade - Martinique" + }, + { + "product_name" : "Oranges - Fair trade - Italy - brand3" + }, + { + "product_name" : "Oranges - Organic - Spain - brand1, brand2, brand3" + } + ], + "skip" : 0 +} diff --git a/tests/integration/expected_test_results/facets/label_fair-trade_label_-organic.json b/tests/integration/expected_test_results/facets/label_fair-trade_label_-organic.json new file mode 100644 index 0000000000000..3d8e5a625895a --- /dev/null +++ b/tests/integration/expected_test_results/facets/label_fair-trade_label_-organic.json @@ -0,0 +1,15 @@ +{ + "count" : 2, + "page" : 1, + "page_count" : 2, + "page_size" : 24, + "products" : [ + { + "product_name" : "Carrots - Fair trade - Italy - brand1, brand2" + }, + { + "product_name" : "Oranges - Fair trade - Italy - brand3" + } + ], + "skip" : 0 +} diff --git a/tests/integration/facets.t b/tests/integration/facets.t new file mode 100644 index 0000000000000..46c28c434ceb7 --- /dev/null +++ b/tests/integration/facets.t @@ -0,0 +1,271 @@ +#!/usr/bin/perl -w + +use ProductOpener::PerlStandards; + +use Test::More; +use ProductOpener::APITest qw/:all/; +use ProductOpener::Test qw/:all/; +use ProductOpener::TestDefaults qw/:all/; + +use File::Basename "dirname"; + +use Storable qw(dclone); + +remove_all_users(); + +remove_all_products(); + +wait_application_ready(); + +my $ua = new_client(); + +my %create_user_args = (%default_user_form, (email => 'bob@gmail.com')); +create_user($ua, \%create_user_args); + +# Create some products to test facets + +my @products = ( + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000001', + product_name => "Carrots - Organic - France - brand1, brand2", + categories => "en:carrots", + labels => "en:organic", + origins => "en:france", + brands => 'brand1, brand2', + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000002', + product_name => "Carrots - Fair trade, Organic - France - brand1", + categories => "en:carrots", + labels => "en:organic, en:fair-trade", + origins => "en:france", + brands => 'brand1', + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000003', + product_name => "Carrots - No label - Belgium - brand2", + categories => "en:carrots", + origins => "en:belgium", + brands => 'brand2', + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000004', + product_name => "Carrots - Fair trade - Italy - brand1, brand2", + categories => "en:carrots", + labels => "en:fair-trade", + origins => "en:italy", + brands => 'brand1, brand2', + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000005', + product_name => "Bananas - Organic - France - brand2", + categories => "en:bananas", + labels => "en:organic", + origins => "en:france", + brands => 'brand2', + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000006', + product_name => "Bananas - Organic - Martinique - brand1", + categories => "en:bananas", + labels => "en:organic", + origins => "en:martinique", + brands => 'brand1', + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000007', + product_name => "Oranges - Organic - Spain - brand1, brand2, brand3", + categories => "en:oranges", + labels => "en:organic", + origins => "en:spain", + brands => 'brand1, brand2, brand3', + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000008', + product_name => "Oranges - Fair trade - Italy - brand3", + categories => "en:oranges", + labels => "en:fair-trade", + origins => "en:italy", + brands => 'brand3', + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000009', + product_name => "Apples - Organic - France", + categories => "en:apples", + labels => "en:organic", + origins => "en:france", + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000010', + product_name => "Apples - Organic, Fair trade - France", + categories => "en:apples", + labels => "en:organic,en:fair-trade", + origins => "en:france", + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000011', + product_name => "Apples - Organic - France, Belgium, Canada", + categories => "en:apples", + labels => "en:organic", + origins => "en:france,en:belgium,en:canada", + ), + }, + { + %{dclone(\%empty_product_form)}, + ( + code => '200000000012', + product_name => "Chocolate - Organic, Fair trade - Martinique", + categories => "en:chocolate", + labels => "en:organic,en:fair-trade", + origins => "en:martinique", + ), + }, +); + +foreach my $product_ref (@products) { + edit_product($ua, $product_ref); +} + +# Note: expected results are stored in json files, see execute_api_tests +# We use the API with .json to test facets, in order to easily get the products that are returned +my $tests_ref = [ + { + test_case => 'brand_brand1', + method => 'GET', + path => '/brand/brand1.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'brand_-brand1', + method => 'GET', + path => '/brand/-brand1.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'brand_brand2_brand_-brand1', + method => 'GET', + path => '/brand/brand2/brand/-brand1.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'category_apples', + method => 'GET', + path => '/category/apples.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'category_-apples', + method => 'GET', + path => '/category/-apples.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'category_apples_label_organic', + method => 'GET', + path => '/category/apples/label/organic.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'category_-apples_label_organic', + method => 'GET', + path => '/category/-apples/label/organic.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'category_-apples_label_-organic', + method => 'GET', + path => '/category/-apples/label/-organic.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'label_fair-trade_label_-organic', + method => 'GET', + path => '/label/fair-trade/label/-organic.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + # test with 3 facets + { + test_case => 'category_bananas_label_organic_brand_brand1', + method => 'GET', + path => '/category/bananas/label/organic/brand/brand1.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'category_bananas_label_organic_brand_-brand1', + method => 'GET', + path => '/category/bananas/label/organic/brand/-brand1.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + # Special unknown value, should match unexisting or empty tags array + { + test_case => 'brand_unknown', + method => 'GET', + path => '/brand/unknown.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + { + test_case => 'brand_-unknown', + method => 'GET', + path => '/brand/-unknown.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, + # Contributor + { + test_case => 'contributor_tests', + method => 'GET', + path => '/contributor/tests.json?fields=product_name', + expected_status_code => 200, + sort_products_by => 'product_name', + }, +]; + +# note: we need to execute the tests with bob, because we need authentication +# to see data quality panels +execute_api_tests(__FILE__, $tests_ref, $ua); + +done_testing(); diff --git a/tests/unit/display.t b/tests/unit/display.t index 06e2ecdadef96..27dae373add04 100644 --- a/tests/unit/display.t +++ b/tests/unit/display.t @@ -29,20 +29,6 @@ is(display_date_tag($t), '