Schema.org Markup for Products

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
Il y a 11 ans
I have implemented the markup from Schema.org for products on one of my NC sites (with the exception of ratings & reviews, and Variants in Grid). I'm sure it can be expanded on, but I thought I would post what I have done so far.

The code below is for v2.80:

(Views/Catalog/Product.Tempalte.SingleVariant.cshtml)

<div class="page product-details-page">
    <div class="page-body">
        @Html.Widget("productdetails_top")
        @using (Html.BeginRouteForm("Product", new { SeName = Model.SeName }, FormMethod.Post, new { id = "product-details-form" }))
        {
            <span itemscope itemtype="http://schema.org/Product">
            <div class="product-essential">
                @Html.Widget("productdetails_before_pictures")
                <!--product pictures-->
                @Html.Partial("_ProductDetailsPictures", Model)
                @Html.Widget("productdetails_after_pictures")
                <div class="overview">
                    <h1 class="product-name">
                        <span itemprop="name">@Model.Name</span>
                    </h1>
                    <div class="short-description">
                        @Html.Raw(Model.ShortDescription)
                    </div>
                    <div class="clear">
                    </div>
                    @Html.Widget("productdetails_overview_top")
                    <!--product SKU, manufacturer part number, stock info-->
                    @Html.Partial("_ProductVariant_SKU_Man_Stock", defaultProductVariant)
                    <div class="clear">
                    </div>
                    <!--Back in stock subscription-->
                    @Html.Partial("_ProductVariantBackInStockSubscription", defaultProductVariant)
                    <div class="clear">
                    </div>
                    <!--product manufacturers-->
                    @Html.Action("ProductManufacturers", "Catalog", new { productId = Model.Id })
                    <div class="clear">
                    </div>
                    <!--product reviews-->
                    @Html.Action("ProductReviewOverview", "Catalog", new { productId = Model.Id })
                    <div class="clear">
                    </div>
                    <!--sample download-->
                    @Html.Partial("_DownloadSample", defaultProductVariant)
                    <div class="clear">
                    </div>
                    @{
            var dataDictPrice = new ViewDataDictionary();
            dataDictPrice.TemplateInfo.HtmlFieldPrefix = string.Format("price_{0}", defaultProductVariant.Id);
                        @Html.Partial("_ProductVariantPrice", defaultProductVariant.ProductVariantPrice, dataDictPrice)
                            
            var dataDictAddToCart = new ViewDataDictionary();
            dataDictAddToCart.TemplateInfo.HtmlFieldPrefix = string.Format("addtocart_{0}", defaultProductVariant.Id);
                        @Html.Partial("_ProductVariantAddToCart", defaultProductVariant.AddToCart, dataDictAddToCart)                    
                    }
                    <div class="clear">
                    </div>
                    @Html.Action("ProductEmailAFriendButton", "Catalog", new { productId = Model.Id })
                    @Html.Action("CompareProductsButton", "Catalog", new { productId = Model.Id })
                    <div class="clear">
                    </div>
                    @Html.Action("ShareButton", "Catalog")
                    @Html.Widget("productdetails_overview_bottom")
                </div>
                <div class="full-description">
                    <span itemprop="description">@Html.Raw(Model.FullDescription)</span>
                </div>
            </div>
            <div class="clear">
            </div>
            <div class="product-collateral">
                <div class="product-variant-line">
                    <!--product tier prices-->
                    @Html.Action("ProductTierPrices", "Catalog", new { productVariantId = defaultProductVariant.Id })
                    <div class="clear">
                    </div>
                    @{
            var dataDictAttributes = new ViewDataDictionary();
            dataDictAttributes.TemplateInfo.HtmlFieldPrefix = string.Format("attributes_{0}", defaultProductVariant.Id);
                        @Html.Partial("_ProductAttributes", defaultProductVariant.ProductVariantAttributes, dataDictAttributes)                  
                    }
                    <div class="clear">
                    </div>
                    @{
            var dataDictGiftCard = new ViewDataDictionary();
            dataDictGiftCard.TemplateInfo.HtmlFieldPrefix = string.Format("giftcard_{0}", defaultProductVariant.Id);
                        @Html.Partial("_GiftCardInfo", defaultProductVariant.GiftCard, dataDictGiftCard)
                    }
                </div>
                @Html.Action("ProductSpecifications", "Catalog", new { productId = Model.Id })
                <div class="clear">
                </div>
                @Html.Action("ProductTags", "Catalog", new { productId = Model.Id })
                <div class="clear">
                </div>
                @Html.Action("ProductsAlsoPurchased", "Catalog", new { productId = Model.Id })
                <div class="clear">
                </div>
                @Html.Action("RelatedProducts", "Catalog", new { productId = Model.Id })
                <div class="clear">
                </div>
            </div>
                </span>
        }
        @Html.Widget("productdetails_bottom")
    </div>
</div>
Il y a 11 ans
(Views/Catalog/_ProductVariant_SKU_Man_Stock.cshtml)

@model ProductDetailsModel.ProductVariantModel
@using Nop.Web.Models.Catalog;
@*SKU*@
@if (!String.IsNullOrWhiteSpace(Model.Sku) && Model.ShowSku)
{
    <div class="sku">
        <span class="label">@T("Products.Sku"): </span><span class="value"><span itemprop="sku">@Model.Sku</span></span>
    </div>
    <div class="clear">
    </div>
}
@*Manufacturer part number*@
@if (!String.IsNullOrWhiteSpace(Model.ManufacturerPartNumber) && Model.ShowManufacturerPartNumber)
{
    <div class="manufacturer-part-number">
        <span class="label">@T("Products.ManufacturerPartNumber"): </span><span class="value">@Model.ManufacturerPartNumber</span>
    </div>
    <div class="clear">
    </div>
}
@*GTIN*@
@if (!String.IsNullOrWhiteSpace(Model.Gtin) && Model.ShowGtin)
{
    <div class="gtin">
        <span class="label">@T("Products.GTIN"): </span><span class="value">@Model.Gtin</span>
    </div>
    <div class="clear">
    </div>
}
@*Stock availability*@
@if (!String.IsNullOrWhiteSpace(Model.StockAvailability))
{
    <div class="stock">
        <span class="label">@T("Products.Availability"): </span><span class="value">@Model.StockAvailability</span>
    </div>
}
Il y a 11 ans
(Views/Catalog/_ProductVariantPrice.cshtml)

@model ProductDetailsModel.ProductVariantModel.ProductVariantPriceModel
@using Nop.Web.Models.Catalog;
@using Nop.Core.Domain.Catalog;
@using Nop.Core.Infrastructure;
@using Nop.Core;
@using System.Text;
@using System.Globalization;
@using Nop.Services.Customers;
@using System.Text.RegularExpressions;
@{
    string pattern = "(?<val>(\\d+[\\s\\,\\.]?)+)";
    string replacement = String.Format("<span class=\"price-val-for-dyn-upd-{0}\">${{val}}</span> ", Model.ProductVariantId);  
}
@if (Model.DynamicPriceUpdate)
{
    <script type="text/javascript">
        var priceValForDynUpd_@(Model.ProductVariantId) = @(((float)(Model.PriceValue != Model.PriceWithDiscountValue ? Model.PriceWithDiscountValue : Model.PriceValue)).ToString(new CultureInfo("en-US")));
    </script>
}
<div class="prices"><div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
    @if (Model.CustomerEntersPrice)
    {
        /*display nothing*/
    }
    else if (Model.CallForPrice)
    {
        <span class="product-price">@T("Products.CallForPrice")</span>
    }
    else
    {
        if (!String.IsNullOrWhiteSpace(Model.OldPrice))
        {
        @T("Products.Price.OldPrice")<text>:</text>
        <span class="old-product-price">@Model.OldPrice</span>
        <br />
        }
        if (!String.IsNullOrWhiteSpace(Model.OldPrice) ||
            !String.IsNullOrWhiteSpace(Model.PriceWithDiscount))
        {
        @T("Products.Price")<text>:</text>
        }
        
        
        <span
        @if (String.IsNullOrWhiteSpace(Model.PriceWithDiscount))
        {
            <text>class="product-price"</text>
        }
        >
            @if (Model.DynamicPriceUpdate && !Model.HidePrices && String.IsNullOrWhiteSpace(Model.PriceWithDiscount))
            {
                @Html.Raw(Regex.Replace(Model.Price, pattern, replacement))
            }
            else
            {
                <span itemprop="price">@Html.Raw(Model.Price)</span>
            }
        </span>
        
        
        
            if (!String.IsNullOrWhiteSpace(Model.PriceWithDiscount))
            {
        <br />
        @T("Products.Price.WithDiscount")<text>:</text>
        <span class="product-price">
            @if (Model.DynamicPriceUpdate && !Model.HidePrices)
            {
                @Html.Raw(Regex.Replace(Model.PriceWithDiscount, pattern, replacement))
            }
            else
            {
                @Html.Raw(Model.PriceWithDiscount)
            }
        </span>
            }
    }
</div>
</div>
Il y a 11 ans
(Views/Catalog/_ProductsDetailsPictures.cshtml)

@model ProductDetailsModel
@using Nop.Web.Models.Catalog;
@{
    Html.AddScriptParts("~/Scripts/slimbox2.js");

    int productPerRow = 6;
}
<meta itemprop="image" content="@Model.DefaultPictureModel.ImageUrl" />
<div class="picture">
    @if (Model.DefaultPictureZoomEnabled)
    {
        <a href="@Model.DefaultPictureModel.FullSizeImageUrl" data-gallery="lightbox-pd" title="@Model.Name">
            <img alt="@Model.DefaultPictureModel.AlternateText" src="@Model.DefaultPictureModel.ImageUrl" title="@Model.DefaultPictureModel.Title" />
        </a>
    }
    else
    {
        <img alt="@Model.DefaultPictureModel.AlternateText" src="@Model.DefaultPictureModel.ImageUrl" title="@Model.DefaultPictureModel.Title" />
    }
    @if (Model.PictureModels.Count > 1)
    {
        <table class="picture-thumbs">
            @for (int i = 0; i < Model.PictureModels.Count; i++)
            {
                var picture = Model.PictureModels[i];

                if (i % productPerRow == 0)
                {
                @Html.Raw("<tr>")
                }
                    
                <td class="a-left">
                    <a href="@picture.FullSizeImageUrl" data-gallery="lightbox-p" title="@Model.Name">
                        <img src="@picture.ImageUrl" alt="@picture.AlternateText" title="@picture.Title" />
                    </a>
                </td>
                                    
                if ((i % productPerRow == (productPerRow - 1)) ||
                    //last image
                    (i == (Model.PictureModels.Count - 1)))
                {
                @Html.Raw("</tr>")
                }
            }
        </table>
    }
</div>
Il y a 11 ans
Hi Blake,

Thanks a lot!
Il y a 11 ans
And here we go. Please see changeset ee4cf473a13a
Il y a 11 ans
a.m. wrote:
And here we go. Please see changeset ee4cf473a13a

That will save me a great deal of time with upgrades! Thank you!
Il y a 11 ans
Great stuff, i've been manually amending my breadcrumbs so they get picked up on Google.

Cheers,

Dave
Il y a 11 ans
a.m. wrote:
And here we go. Please see changeset ee4cf473a13a


is there a changeset regarding Schema.org Markup / Rich Snippet for Mobile ?

i think this change should also be in file : CategoryTemplate.ProductsInGridOrLines.cshtml

Thanks,
Shay
Il y a 11 ans
For all you multi currency store owners.
You can also add the currency to _ProductVariantPrice.cshtml

Inside your div or span -  itemprop="offers" itemscope itemtype="http://schema.org/Offer"

add this line
<meta itemprop="currency" content='@(WorkContext.WorkingCurrency.CurrencyCode)' />
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.