Preparing the Master Page - FadiZahhar/umbraco8showandtell GitHub Wiki

1. Creating "Site Settings" in Umbraco 8

Step 1: Create a "Site Settings" Document Type

  • Go to Settings > Document Types.

  • Create a new document type:

    • Name: Site Settings
    • Alias: siteSettings
    • Make it not allowed as a root node.
  • Add properties you need for global use. Common examples:

    • Site Logo (Image Picker)
    • Footer Text (Textbox/textarea)
    • Copyright Notice (Textbox)
    • Cookie Message (Textarea)
    • Social Links (Link picker or nested content)

Step 2: Add a "Site Settings" Node

  • In the Content section, create a new node of type Site Settings as a child (or sibling) to your Home node.
  • Fill in all the values you want to display globally.

Step 3: Access Site Settings from Views

  • Use Umbraco’s helper methods to retrieve this node globally (typically by querying for the first node of type siteSettings).
// _siteSettings = Umbraco.ContentAtRoot().First().Children().First(x => x.ContentType.Alias == "siteSettings");

Or, for a small site, you can store the node’s ID in web.config or as a constant.


2. Adding References to the Master Page

Step 1: Update the Master Layout (main.cshtml)

  • Find your ~/Views/main.cshtml file (or create it).
  • Add references to your CSS and JS here:
<!DOCTYPE html>
<html>
<head>
    @RenderSection("head", false)
    <link rel="stylesheet" href="~/css/main.min.css" />
</head>
<body>
    @RenderBody()
    <script src="~/js/main.min.js"></script>
    @RenderSection("scripts", required: false)
</body>
</html>

Step 2: Reference Site Settings in the Layout

At the top of your layout, add:

@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
    var home = Umbraco.ContentAtRoot().FirstOrDefault();
    var siteSettings = home?.Children.FirstOrDefault(x => x.ContentType.Alias == "siteSettings");
}

Now you can use siteSettings anywhere in your layout, e.g.

<img src="@siteSettings.Value<IPublishedContent>("siteLogo")?.Url" alt="Site Logo" />

3. Bundling and Minification

Step 1: Install a Bundling Package (Optional)

Step 2: Reference Minified Assets

  • In your layout, always reference the .min.css/.min.js files.
  • (If you use Smidge, you can configure and render bundles using Smidge’s helpers in your layout.)
    main.cshtml - Main layout view for the HighlyDeveloped.Web application.

    - Inherits from UmbracoViewPage, enabling access to Umbraco content and helpers.
    - Uses ClientDependency.Core.Mvc for managing CSS and JS dependencies.
    - Sets Layout to null, making this a root layout.
    - Retrieves the home page content using Model.AncestorOrSelf<Home>().

    Head Section:
    - Registers external and local CSS files (Bootstrap, Font Awesome, CookieConsent).
    - Registers external and local JS files (jQuery, Popper.js, Bootstrap, Font Awesome, CookieConsent).
    - Uses integrity and crossorigin attributes for CDN resources to enhance security.
    - Renders all required CSS with @Html.RenderCssHere().

    Body Section:
    - Renders the navigation bar via a partial view ("NavBar").
    - Renders the main body content with @RenderBody().
    - Renders the footer via a partial view ("Footer").
    - Displays a cookie consent alert with a call-to-action button.
    - Renders all required JS with @Html.RenderJsHere().

    Note:
    - This layout is designed for use with Umbraco 8 and leverages best practices for dependency management and partial rendering.


4. Adding Site Navigation

Step 1: Create a Partial View for Navigation

  • In Settings > Partial Views, create a new partial view called NavBar.cshtml.

Example for a top-level navigation:

Renders a responsive navigation bar using Bootstrap classes.
- Inherits from UmbracoViewPage to access Umbraco content features.
- Retrieves the home page node using Model.AncestorOrSelf<Home>().
- Iterates through the direct children of the home page to generate navigation links.
- Each child page is rendered as a navigation item with its name and URL.
- The navigation bar is styled with Bootstrap's dark theme and is mobile-friendly with a collapsible menu.
@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
    var homePage = Model.AncestorOrSelf<Home>();
}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="container">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav ml-auto">

                @foreach (var page in homePage.Children)
                {
                    <li class="nav-item">
                        <a class="nav-link" href="@page.Url">@page.Name</a>
                    </li>
                }
                
            </ul>
        </div>
    </div>
</nav>

Step 2: Render Navigation Partial in Layout

  • In main.cshtml, place:
@Html.Partial("navBar")

where you want the navigation menu to appear.


5. Make Navigation Dynamic (Multi-level)

For dropdowns or submenus, extend the logic:

<ul>
@foreach (var item in menuItems)
{
    <li>
        <a href="@item.Url">@item.Name</a>
        @{
            var children = item.Children.Where(x => x.IsVisible());
            if(children.Any())
            {
                <ul>
                    @foreach (var child in children)
                    {
                        <li><a href="@child.Url">@child.Name</a></li>
                    }
                </ul>
            }
        }
    </li>
}
</ul>

6. Refactor Navigation as a Partial View

  • Keep all navigation logic in navBar.cshtml.
  • Optionally, add parameters (with @model) to control how deep the nav goes, styling, etc.

7. Add the Footer as a Partial View

Step 1: Create Footer.cshtml

  • In Partial Views, create Footer.cshtml.
  • Use site settings, e.g.:
@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
    var home = Umbraco.ContentAtRoot().FirstOrDefault();
    var siteSettings = home?.Children.FirstOrDefault(x => x.ContentType.Alias == "siteSettings");
}
<footer>
    <p>@siteSettings.Value("footerText")</p>
    <p>&copy; @DateTime.Now.Year @siteSettings.Value("copyrightNotice")</p>
</footer>

Step 2: Render the Footer

  • In main.cshtml:
@Html.Partial("Footer")

8. Add a Cookie Consent Banner

Step 1: Add Cookie Consent HTML

  • In your layout, above the closing </body> tag:
<div id="cookieConsent" style="display:none;position:fixed;bottom:0;width:100%;background:#222;color:#fff;padding:1rem;text-align:center;z-index:999;">
    @siteSettings.Value("cookieMessage") ?? "This website uses cookies to ensure you get the best experience."
    <button onclick="acceptCookies()" style="margin-left:10px;">Accept</button>
</div>

Step 2: Add JS to Handle Consent

<script>
function acceptCookies() {
    document.getElementById('cookieConsent').style.display = 'none';
    document.cookie = "cookieAccepted=true; path=/; max-age=31536000";
}
window.onload = function() {
    if (!document.cookie.includes('cookieAccepted=true')) {
        document.getElementById('cookieConsent').style.display = 'block';
    }
}
</script>

You can move the script to your main.min.js for better bundling.


9. Review and Test

  • Make sure all partials are rendering as expected.
  • Test the site settings, navigation, and cookie consent.
  • Check responsive behavior.

Summary Table

Feature File/Location Description
Site Settings Document Type + Node Global values for the site
Master Page /Views/Shared/main.cshtml CSS/JS references, nav/footer
Bundling/Minification Bundling tool or minified files Performance optimization
Dynamic Navigation Partial View navBar Dynamic, multi-level nav menu
Footer Partial View Footer Shared site footer
Cookie Consent Layout + JS Cookie message with "Accept"

Here’s what I found in your archive:

Root:

  • Web.config

  • Web.Debug.config

  • Web.Release.config

Views:

  • About.cshtml

  • main.cshtml

  • Home.cshtml

  • Web.config (for views)

  • Partials/NavBar.cshtml

  • Partials/Footer.cshtml

  • Partials/Grid/… (various editors for grid layout)

This is a classic Umbraco 8/MVC project structure.
Now I’ll analyze the code inside your layout, navigation, and footer partials, then provide tailored implementation steps.


1. Master Page Layout (main.cshtml)

Let’s review your main layout to understand how scripts, styles, navigation, and footer are referenced.

Your main.cshtml layout is already using Umbraco's ClientDependency for script and CSS bundling and minification.
It pulls in Bootstrap, Font Awesome, a CookieConsent stylesheet, and all the JS via Html.RequiresCss and Html.RequiresJs.
The layout starts with:

@inherits Umbraco.Web.Mvc.UmbracoViewPage
@using ClientDependency.Core.Mvc
@{
    Layout = null;
    var homePage = Model.AncestorOrSelf<Home>();
}

And then bundles styles/scripts in the <head>.


2. Navigation Partial (Views/Partials/NavBar.cshtml)

Let’s review how navigation is generated.

Your navigation partial (NavBar.cshtml) dynamically lists all children of the home page as menu items:

@foreach (var page in homePage.Children)
{
    <li class="nav-item">
        <a class="nav-link" href="@page.Url">@page.Name</a>
    </li>
}

This means your navigation will update automatically based on the child pages of your Home node—already dynamic!


3. Footer Partial (Views/Partials/Footer.cshtml)

Let’s check your footer for global, editable values or placeholders for future site settings.

Your Footer partial is simple—static social links and a copyright notice:

<ul class="list-inline-item">
    <li class="list-inline-item">
        <a href="https://twitter.com/hdeveloped" ...>
            <i class="fab fa-twitter fa-2x text-white"></i>
        </a>
    </li>
</ul>
<p class="text-white">Copyright &copy; @DateTime.Now.Year Highly Developed</p>

It could easily be improved to pull values (like copyright, social links, or footer text) from Site Settings.


4. Cookie Consent

You reference /css/CookieConsent.css and probably include cookie consent logic via a script in your assets.
I'll show you how to make sure this works, and how to pull its message from Site Settings.


Now, Here’s a Tailored Step-by-Step Guide (Based on Your Current Code)


Step 1: Create "Site Settings" for Global Values

a. In Umbraco Backoffice:

  • Create a Site Settings document type (not allowed at root).

    • Add properties:

      • Footer Text (Textbox)

      • Copyright Notice (Textbox)

      • Cookie Message (Textarea)

      • Social Links (e.g. Nested Content or Multinode Picker)

  • In Content, add a Site Settings node under Home.


Step 2: Access Site Settings in Your Views

At the top of main.cshtml, NavBar.cshtml, and Footer.cshtml, add:

@{
    var homePage = Model.AncestorOrSelf<Home>();
    var siteSettings = homePage.Children.FirstOrDefault(x => x.ContentType.Alias == "siteSettings");
}

You can now use siteSettings.Value<string>("propertyAlias") to output values.


Step 3: Make Footer Dynamic

Edit Footer.cshtml to use Site Settings:

@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
    var homePage = Model.AncestorOrSelf<Home>();
    var siteSettings = homePage.Children.FirstOrDefault(x => x.ContentType.Alias == "siteSettings");
}
<footer class="bg-dark">
    <div class="container">
        <div class="row">
            <div class="col text-center py-4">
                <!-- Example Social Links Loop -->
                <ul class="list-inline-item">
                    @* Loop through social links if configured *@
                    @{
                        var socials = siteSettings?.Value<IEnumerable<IPublishedElement>>("socialLinks");
                        if (socials != null) {
                            foreach(var link in socials) {
                                <li class="list-inline-item">
                                    <a href="@link.Value("url")" target="_blank" title="@link.Value("name")">
                                        <i class="@link.Value("iconClass") fa-2x text-white"></i>
                                    </a>
                                </li>
                            }
                        }
                    }
                </ul>
                <p class="text-white">
                  @siteSettings?.Value("footerText")<br/>
                  &copy; @DateTime.Now.Year @siteSettings?.Value("copyrightNotice")
                </p>
            </div>
        </div>
    </div>
</footer>

Adjust the property aliases to match your Site Settings setup.


Step 4: Add Site Settings Reference to Layout

In your main.cshtml, you already include all scripts/styles via Html.RequiresCss/Js (bundling & minification are handled by Umbraco’s ClientDependency out of the box).
Just ensure you always retrieve siteSettings as above, to make it available everywhere.


Step 5: Cookie Consent from Site Settings

At the bottom of your main.cshtml (just before </body>), add:

<div id="cookieConsent" style="display:none;position:fixed;bottom:0;width:100%;background:#222;color:#fff;padding:1rem;text-align:center;z-index:999;">
    @siteSettings?.Value("cookieMessage") ?? "This website uses cookies to ensure you get the best experience."
    <button onclick="acceptCookies()" style="margin-left:10px;">Accept</button>
</div>
<script>
function acceptCookies() {
    document.getElementById('cookieConsent').style.display = 'none';
    document.cookie = "cookieAccepted=true; path=/; max-age=31536000";
}
window.onload = function() {
    if (!document.cookie.includes('cookieAccepted=true')) {
        document.getElementById('cookieConsent').style.display = 'block';
    }
}
</script>

Link your CookieConsent.css for styling.


Step 6: Navigation Refactoring (Dynamic & Multi-level)

Your NavBar.cshtml is already dynamic.
To support sub-menus, you can enhance the loop:

@foreach (var page in homePage.Children)
{
    <li class="nav-item @(page.Children.Any() ? "dropdown" : "")">
        <a class="nav-link @(page.Children.Any() ? "dropdown-toggle" : "")" 
           href="@page.Url"
           @(page.Children.Any() ? "data-toggle=\"dropdown\"" : "")>
            @page.Name
        </a>
        @if(page.Children.Any()) {
            <div class="dropdown-menu">
                @foreach(var child in page.Children) {
                    <a class="dropdown-item" href="@child.Url">@child.Name</a>
                }
            </div>
        }
    </li>
}

Step 7: Bundling & Minification

Already implemented via Html.RequiresCss/Js with ClientDependency!
To add your own assets, use:

Html.RequiresCss("/css/custom.min.css");
Html.RequiresJs("/js/custom.min.js");

Summary Table (Specific to Your Code)

Task Where Code/Logic
Site Settings Doc Type Backoffice Contains global values for use in all partials
Site Settings in Views Top of each Partial/Layout var siteSettings = ...
Dynamic Navigation NavBar.cshtml @foreach (var page in homePage.Children)
Dynamic Footer Footer.cshtml Use siteSettings properties for copyright/socials
Cookie Consent main.cshtml (bottom) Use siteSettings + JS for banner
Bundling/Minification main.cshtml Html.RequiresCss/Js (ClientDependency)
⚠️ **GitHub.com Fallback** ⚠️