Schema.org Markup for Products

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
11 years ago
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>
11 years ago
(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>
}
11 years ago
(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>
11 years ago
(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>
11 years ago
Hi Blake,

Thanks a lot!
11 years ago
And here we go. Please see changeset ee4cf473a13a
11 years ago
a.m. wrote:
And here we go. Please see changeset ee4cf473a13a

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

Cheers,

Dave
11 years ago
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
10 years ago
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.