diff --git a/cgi/user.pl b/cgi/user.pl index a5ecfb42d23c0..f9cceefce3116 100644 --- a/cgi/user.pl +++ b/cgi/user.pl @@ -166,7 +166,7 @@ $template_data_ref->{sections} = []; - if ($user_ref) { + if ($user_ref) { push @{$template_data_ref->{sections}}, { id => "user", fields => [ @@ -236,12 +236,35 @@ push @{$team_section_ref->{fields}}, { field => "team_". $i, label => sprintf(lang("team_s"), $i), - }; + }; }; push @{$template_data_ref->{sections}}, {%$team_section_ref}; } + # Contributor section + my $contributor_section_ref = { + id => "contributor_settings", + name => lang("contributor_settings") . " (" . lang("optional") . ")", + description => "contributor_settings_description", + fields => [ + { + field => "display_barcode", + type => "checkbox", + label => display_icon("barcode") . lang("display_barcode_in_search"), + value => $user_ref->{display_barcode} && "on", + }, + { + field => "edit_link", + type => "checkbox", + label => display_icon("edit") . lang("edit_link_in_search"), + value => $user_ref->{edit_link} && "on", + }, + ] + }; + + push @{$template_data_ref->{sections}}, {%$contributor_section_ref}; + # Admin section if ($admin) { my $administrator_section_ref = { diff --git a/docker/dev.yml b/docker/dev.yml index 7b2fd9f621629..859e5af93dd0a 100644 --- a/docker/dev.yml +++ b/docker/dev.yml @@ -71,6 +71,10 @@ services: USER_GID: ${USER_GID:-1000} volumes: - ./html:/opt/product-opener/html/ + - ./icons:/opt/product-opener/icons + - ./scss:/opt/product-opener/scss + - ./gulpfile.js:/opt/product-opener/ + - ./snyk:/opt/product-opener/ mongodb: # Note: keep it as close a possible to mongodb.yml image: mongo:4.4 diff --git a/html/js/product-search.js b/html/js/product-search.js index 53e8ce728499c..07cdaa0474b0c 100644 --- a/html/js/product-search.js +++ b/html/js/product-search.js @@ -140,9 +140,14 @@ function rank_products(products, product_preferences, use_user_product_preferenc } -function display_products(target, product_groups, use_user_product_preferences_for_ranking ) { +function product_edit_url(product) { + return `/cgi/product.pl?type=edit&code=${product.code}`; +} + + +function display_products(target, product_groups, user_prefs ) { - if (use_user_product_preferences_for_ranking) { + if (user_prefs.use_ranking) { $( target ).html('' + '
'); } @@ -157,52 +162,66 @@ function display_products(target, product_groups, use_user_product_preferences_f $.each( product_group, function(key, product) { var product_html = ""; - + // Show the green / grey / colors for matching products only if we are using the user preferences - if (use_user_product_preferences_for_ranking) { - product_html += '
  • '; - } - else { - product_html += '
  • '; + let css_classes = 'list_product_a'; + if (user_prefs.use_ranking) { + css_classes += ' list_product_a_match_' + product.match_status; } + product_html += `
  • `; product_html += '
    '; - - if (product.image_front_thumb_url) { - product_html += ''; - } - else { - product_html += ''; - } - + + const img_src = + product.image_front_thumb_url || + "/images/icons/product-silhouette-transparent.svg" + ; + product_html += ``; + product_html += "
    "; - + if (product.product_display_name) { product_html += '
    ' + product.product_display_name + "
    "; } else { product_html += '
    ' + product.code + "
    "; } - + $.each(product.match_attributes.mandatory.concat(product.match_attributes.very_important, product.match_attributes.important), function (key, attribute) { - + if (attribute.icon_url) { var title = attribute.title; - + if (attribute.description_short) { title += ' - ' + attribute.description_short; } if (attribute.missing) { title += " - " + attribute.missing; - } - + } + product_html += ''; } }); - - product_html += "
  • "; - - products_html.push(product_html); + // add some specific fields + if (user_prefs.display.display_barcode) { + product_html += `${product.code}`; + } + product_html += ""; + if (user_prefs.display.edit_link) { + const edit_url = product_edit_url(product); + const edit_title = lang().edit_product_page; + product_html += ` + + + + `; + } + product_html += ""; + + products_html.push(product_html); }); var active = ""; @@ -217,11 +236,11 @@ function display_products(target, product_groups, use_user_product_preferences_f } } else { - text_or_icon = '' + text_or_icon = '' + ' ' + product_group.length + ""; } - if (use_user_product_preferences_for_ranking) { + if (user_prefs.use_ranking) { $("#products_tabs_titles").append( '
  • ' + '' @@ -298,20 +317,21 @@ function display_product_summary(target, product) { } -function rank_and_display_products (target, products) { - +function rank_and_display_products (target, products, contributor_prefs) { + + // Retrieve user preferences from local storage - var user_product_preferences = get_user_product_preferences(); - + var user_prefs = { + use_ranking: JSON.parse(localStorage.getItem('use_user_product_preferences_for_ranking')), + product: get_user_product_preferences(), + display: contributor_prefs, + }; + // Retrieve whether we should use the preferences for ranking, or only for displaying the products - - var use_user_product_preferences_for_ranking = JSON.parse(localStorage.getItem('use_user_product_preferences_for_ranking')); - - var ranked_products = rank_products(products, user_product_preferences, use_user_product_preferences_for_ranking); - - display_products(target, ranked_products, use_user_product_preferences_for_ranking); - + var ranked_products = rank_products(products, user_prefs.product, user_prefs.use_ranking); + display_products(target, ranked_products, user_prefs); + $(document).foundation('equalizer', 'reflow'); } diff --git a/icons/barcode.svg b/icons/barcode.svg new file mode 100644 index 0000000000000..aa8ad565fb6eb --- /dev/null +++ b/icons/barcode.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/ProductOpener/Config2_docker.pm b/lib/ProductOpener/Config2_docker.pm index 5eef90eca37f2..720b5df432b37 100755 --- a/lib/ProductOpener/Config2_docker.pm +++ b/lib/ProductOpener/Config2_docker.pm @@ -61,7 +61,10 @@ my $is_localhost = index($po_domain, 'localhost') != -1; $server_domain = $is_localhost && $po_port != '80' ? "$po_domain:$po_port" : $po_domain; @ssl_subdomains = $is_localhost ? qw() : qw(*); -$producers_platform = $ENV{PRODUCERS_PLATFORM} || "0"; +$producers_platform = $ENV{PRODUCERS_PLATFORM} || undef; +if (defined $producers_platform && $producers_platform && $producers_platform != "0") { + $producers_platform = undef; +} # server paths $data_root = "/mnt/podata"; diff --git a/lib/ProductOpener/Display.pm b/lib/ProductOpener/Display.pm index 707b0ed73830a..ada27caa8b364 100644 --- a/lib/ProductOpener/Display.pm +++ b/lib/ProductOpener/Display.pm @@ -4217,6 +4217,11 @@ sub display_search_results($) { if ($search_api_url !~ /\?/) { $search_api_url =~ s/\&/\?/; } + + my $contributor_prefs_json = decode_utf8(encode_json({ + display_barcode => $User{display_barcode}, + edit_link => $User{edit_link}, + })); my $preferences_text = lang("classify_products_according_to_your_preferences"); @@ -4224,6 +4229,7 @@ sub display_search_results($) { JS @@ -4238,7 +4244,9 @@ JS $initjs .= <{structured_response}{products})); } + my $contributor_prefs_json = decode_utf8(encode_json({ + display_barcode => $User{display_barcode}, + edit_link => $User{edit_link}, + })); + + $scripts .= < var page_type = "products"; var preferences_text = "$preferences_text"; +var contributor_prefs = $contributor_prefs_json; var products = $products_json; JS @@ -5340,8 +5355,10 @@ JS ; $initjs .= <'name', -name=>'name', -value=>$user_ref->{name}, -size=>80, -autocomplete=>'name', -override=>1) . "" - . "\n$Lang{email}{$lang}" - . textfield(-name=>'email', -value=>$user_ref->{email}, -size=>80, -autocomplete=>'email', -type=>'email', -override=>1) . "" - . "\n$Lang{username}{$lang}
    " . (($type eq 'edit') ? '': $Lang{username_info}{$lang}) . "" - . (($type eq 'edit') ? $user_ref->{userid} : - ( textfield(-id=>'userid', -name=>'userid', -value=>$user_ref->{userid}, -size=>40, -onkeyup=>"normalize_string_value(this)", -autocomplete=>'username'))) . "" - . "\n$Lang{password}{$lang}" - . password_field(-name=>'password', -value=>$user_ref->{password}, -autocomplete=>'new-password', -override=>1) . "" - . "\n$Lang{password_confirm}{$lang}" - . password_field(-name=>'confirm_password', -value=>$user_ref->{password}, -autocomplete=>'new-password', -override=>1) . "" - ; - - # Professional account - - $html .= "\n" . lang("pro_account") . ""; - - $html .= "

    " . lang("if_you_work_for_a_producer") . "

    " - . "

    " . lang("producers_platform_description_long") . "

    "; - - my $teams_display = ''; - - if ( ( defined $user_ref->{org} ) and ( $user_ref->{org} ne "" ) ) { - - # Existing user with an accepted organization - - $html .= "

    " - . sprintf( - lang("this_is_a_pro_account_for_org"), - "" . $user_ref->{org} . "" - ) . "

    "; - } - - # Pro platform is only for food right now - - elsif ((defined $options{product_type}) and ($options{product_type} eq "food")) { - # New user or existing user without an accepted organization - - my $pro_checked = ''; - my $pro_org_display = 'style="display:none"'; - - # Check the "pro account" checkbox for register screen on the producers platform - - if (((defined $user_ref->{pro}) and ($user_ref->{pro})) - or ((defined $server_options{producers_platform}) and ($type eq "add"))) { - $pro_checked = "checked"; - $pro_org_display = ""; - $teams_display = 'style="display:none"'; - } - - $html .= "" - . "" - . "
    " - . "

    " . lang("producer_or_brand") . separator_before_colon($lc) . ": " - . textfield(-id=>'requested_org', -name=>'requested_org', -value=>$user_ref->{requested_org}, -override=>1) - . "

    "; - - my $requested_org_ref = retrieve_org($user_ref->{requested_org}); - - if ( defined $requested_org_ref ) { - - $html .= "
    " - . "

    " . sprintf(lang("add_user_existing_org"), org_name($requested_org_ref)) . "

    " - . "

    " . lang("add_user_existing_org_pending") . "

    " - . "

    " .lang("please_email_producers") . "

    " - . "
    "; - } - else { - $html .= "

    " . lang("enter_name_of_org") . "

    "; - } - - $html .= "
    "; - - $html .= ""; - - $initjs .= <" . lang("teams") . " (" . lang("optional") . ")

    " - . lang("teams_description") . "

    " . lang("teams_names_warning") . "

    " - . '
    '; - - for (my $i = 1; $i <= 3; $i++) { - $html .= "\n
    " . sprintf(lang("team_s"), $i) . lang("sep") . ":
    " - . textfield(-name=>"team_" . $i, -value=>$user_ref->{"team_" . $i}, -style=>"max-width:20rem", -override=>1, -onkeyup=>"normalize_string_value(this)") . "
    "; - } - - $html .= "
    "; - } - - - ${$scripts_ref} .= < -SCRIPT -; - - return $html; -} - - -sub display_user_form_optional($$) { - - my $type = shift; - my $user_ref = shift; - - my $html = ''; - - # $html .= "\n$Lang{twitter}{$lang}" - # . textfield(-id=>'twitter', -name=>'twitter', -value=>$user_ref->{name}, -size=>80, -override=>1) . ""; - - if ($type eq 'add') { - - $html .= - "\n" . checkbox(-name=>'newsletter', -label=>lang("newsletter_description"), -checked=>'on') . "
    - $Lang{unsubscribe_info}{$lang}"; - } - - return $html; -} - - -sub display_user_form_admin_only($$) { - - my $type = shift; - my $user_ref = shift; - - my $html = ''; - - if (not $admin) { - return ''; - } - - # $html .= "\n$Lang{twitter}{$lang}" - # . textfield(-id=>'twitter', -name=>'twitter', -value=>$user_ref->{name}, -size=>80, -override=>1) . ""; - - if (($type eq 'add') or ($type eq 'edit')) { - - $html .= "\nAdministrator fields"; - - $html .= "\n$Lang{organization}{$lang}" - . textfield(-id=>'org', -name=>'org', -value=>$user_ref->{org}, -size=>80, -autocomplete=>'org', -override=>1) . ""; - - $html .= "\n" . lang("user_groups") . lang("sep") . ":
      "; - - foreach my $group (@user_groups) { - $html .= "
    • " - . checkbox(-name=>"user_group_$group", -label=>" " . lang("user_group_$group") . separator_before_colon($lc) . " " . lang("user_group_${group}_description") - , -checked=>$user_ref->{$group}, -override=>1) . "
    • "; - } - - $html .= ""; - } - - return $html; -} - - sub check_user_form($$$) { my $type = shift; @@ -538,6 +330,10 @@ sub check_user_form($$$) { } } + # contributor settings + $user_ref->{display_barcode} = !! remove_tags_and_quote(param("display_barcode")); + $user_ref->{edit_link} = !! remove_tags_and_quote(param("edit_link")); + # Check input parameters, redisplay if necessary if (length($user_ref->{name}) < 2) { diff --git a/po/common/common.pot b/po/common/common.pot index 8e4405345d217..770a271d7258d 100644 --- a/po/common/common.pot +++ b/po/common/common.pot @@ -4361,6 +4361,22 @@ msgctxt "team_s" msgid "Team %s" msgstr "Team %s" +msgctxt "contributor_settings" +msgid "Contributor" +msgstr "Contributor" + +msgctxt "contributor_settings_description" +msgid "Those settings allow you to personalize some aspect of the website" +msgstr "Those settings allow you to personalize some aspect of the website" + +msgctxt "display_barcode_in_search" +msgid "Display barcode in search results" +msgstr "Display barcode in search results" + +msgctxt "edit_link_in_search" +msgid "Add an edit link in search results" +msgstr "Add an edit link in search results" + # leave the %d, it will be replaced by a number msgctxt "n_products_will_be_exported" msgid "%d products will be exported." diff --git a/po/common/en.po b/po/common/en.po index e8ede7d006550..0357b6a2efd12 100644 --- a/po/common/en.po +++ b/po/common/en.po @@ -4375,6 +4375,22 @@ msgctxt "team_s" msgid "Team %s" msgstr "Team %s" +msgctxt "contributor_settings" +msgid "Contributor" +msgstr "Contributor" + +msgctxt "contributor_settings_description" +msgid "Those settings allow you to personalize some aspect of the website" +msgstr "Those settings allow you to personalize some aspect of the website" + +msgctxt "display_barcode_in_search" +msgid "Display barcode in search results" +msgstr "Display barcode in search results" + +msgctxt "edit_link_in_search" +msgid "Add an edit link in search results" +msgstr "Add an edit link in search results" + msgctxt "brand_owner" msgid "Brand owner" msgstr "Brand owner" diff --git a/po/common/fr.po b/po/common/fr.po index 376c543caa95f..e7e1ac36a2356 100644 --- a/po/common/fr.po +++ b/po/common/fr.po @@ -4234,6 +4234,22 @@ msgctxt "team_s" msgid "Team %s" msgstr "Équipe %s" +msgctxt "contributor_settings" +msgid "Contributeur" +msgstr "Contributeur" + +msgctxt "contributor_settings_description" +msgid "Those settings allow you to personalize some aspect of the website" +msgstr "Ces paramètres vous permettent de personaliser certains aspects du site" + +msgctxt "display_barcode_in_search" +msgid "Display barcode in search results" +msgstr "Afficher le code barre dans les résultats de recherche" + +msgctxt "edit_link_in_search" +msgid "Add an edit link in search results" +msgstr "Ajouter un lien d'édition de chaque résultat" + # leave the %d, it will be replaced by a number msgctxt "n_products_will_be_exported" msgid "%d products will be exported." diff --git a/scss/_off.scss b/scss/_off.scss index 43b411dc03e6e..ae01b96915a49 100644 --- a/scss/_off.scss +++ b/scss/_off.scss @@ -558,6 +558,9 @@ html[dir="rtl"] { // List of products with cards (e.g. search results, facets) +.search_results li { + position: relative; +} .list_product_a { border-radius:8px; @@ -587,6 +590,27 @@ html[dir="rtl"] { background-color:#faa; } +.search_results .list_display_barcode { + color: black; + font-size: .8em; + display: block; // one line for barcode, to keep thing homogeneous +} + +.search_results .list_edit_link img { + height: 1em; +} + +.search_results .list_edit_link { + position: absolute; + top: 0; + right: 0; + padding: 0 1em; + border-radius: .3em; +} + +.search_results .list_edit_link:hover { + background-color: #aaf; +} // Horizontally and vertically center images .list_product_img_div {