Skip to content

Instantly share code, notes, and snippets.

@MerrittMelker
Created August 13, 2015 02:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MerrittMelker/667676e02cb257da3e88 to your computer and use it in GitHub Desktop.
Save MerrittMelker/667676e02cb257da3e88 to your computer and use it in GitHub Desktop.
Example of Sitefinity Feather Navigation Custom widget
@model Ks.Sf.Web.Model.OpaHeaderController.OpaHeaderView
@using Telerik.Sitefinity.Services;
@using Telerik.Sitefinity.Modules.Pages;
@using Telerik.Sitefinity.Frontend.Mvc.Helpers;
@using Ks.Sf.Web.Mvc.Controllers;
@using ServiceStack
@using Telerik.Sitefinity.Frontend.Navigation.Mvc.Models
@{
var searchTextBoxId = Guid.NewGuid();
var searchButtonId = Guid.NewGuid();
var currentTopLevelNodeUrl = string.Empty;
}
@Html.Script(ScriptRef.JQuery, "top", false)
@Html.Script(Url.WidgetContent("Mvc/Scripts/Angular/angular.min.js"), "top")
<div ng-app="opaHeaderApp">
<div ng-controller="opaHeaderCtrl">
<nav class="navbar navbar-default">
<div class=" container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="@Model.RootNodeUrl" class="navbar-brand"><img src="@Model.ImageUrl" class="img-responsive" /></a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<div class="navbar-form navbar-right">
<form>
<div class="inner-addon left-addon search-container">
<span class="glyphicon glyphicon-search"></span>
<input type="text" title="@Html.Resource("SearchInput")" class="form-control" placeholder="Search" value="@Html.Raw(ViewBag.SearchQuery)" id="@Html.Raw(searchTextBoxId)">
<button style="display: none;" id="@Html.Raw(searchButtonId)"></button>
<input type="hidden" data-sf-role="resultsUrl" value="@Model.SearchResultsUrl" />
<input type="hidden" data-sf-role="indexCatalogue" value="@Model.SearchIndex" />
<input type="hidden" data-sf-role="wordsMode" value="AllWords" />
<input type="hidden" data-sf-role="disableSuggestions" value='false' />
<input type="hidden" data-sf-role="minSuggestionLength" value="3" />
<input type="hidden" data-sf-role="suggestionFields" value="Title,Content" />
<input type="hidden" data-sf-role="language" value="" />
<input type="hidden" data-sf-role="suggestionsRoute" value="/restapi/search/suggestions" />
<input type="hidden" data-sf-role="searchTextBoxId" value='@("#" + searchTextBoxId.ToString())' />
<input type="hidden" data-sf-role="searchButtonId" value='@("#" + searchButtonId.ToString())' />
</div>
</form>
</div>
<ul class="nav navbar-nav navbar-right tk-proxima-nova">
@foreach (var node in Model.Nodes)
{
if (node.IsCurrentlyOpened || node.HasChildOpen) { currentTopLevelNodeUrl = node.Url; }
<li>
<div>
@if (node.ChildNodes.Count > 0)
{
<a href="@node.Url" class="@if(node.IsCurrentlyOpened || node.HasChildOpen) {<text>active</text>}">
@node.Title
</a>
<span data-toggle="dropdown" class="glyphicon stage-arrow dropdown-toggle" ng-class="'@node.Url' === currentNodeUrl && subMenuExpanded ? 'glyphicon-menu-up' : 'glyphicon-menu-down'" ng-click="toggle('@node.Url')"></span>
}
else
{
<a class="@if(node.IsCurrentlyOpened || node.HasChildOpen) {<text>active</text>}" href="@node.Url">@node.Title</a>
}
@if (node.ChildNodes.Count > 0)
{
<ul class="dropdown-menu">
@foreach (var childNode in node.ChildNodes)
{
<li><a href="@childNode.Url" target="_self">@childNode.Title</a></li>
}
</ul>
}
</div>
</li>
}
</ul>
</div>
</div>
</nav>
@foreach (var node in Model.Nodes)
{
if (node.ChildNodes.Count > 0)
{
<div class="subNav tk-proxima-nova hidden-md hidden-sm hidden-xs" style="width: 100%;" ng-show="'@node.Url' === currentNodeUrl && subMenuExpanded">
<div class="container">
<ul>
@foreach (var childNode in node.ChildNodes)
{
<li><a class="@if (childNode.IsCurrentlyOpened || childNode.HasChildOpen) {<text>active</text>}" href="@childNode.Url">@childNode.Title</a></li>
}
</ul>
</div>
</div>
}
}
</div>
</div>
<script>
var myApp = angular.module('opaHeaderApp', []);
myApp.controller('opaHeaderCtrl', ['$scope', function ($scope) {
$scope.subMenuExpanded = true;
$scope.nodes = [];
$scope.currentNodeUrl = '@currentTopLevelNodeUrl';
$scope.toggle = function (nodeUrl) {
if (nodeUrl === $scope.currentNodeUrl) {
$scope.subMenuExpanded = !$scope.subMenuExpanded;
} else {
$scope.subMenuExpanded = true;
}
$scope.currentNodeUrl = nodeUrl;
}
}]);
</script>
@Html.Script(Url.WidgetContent("Mvc/Scripts/SearchBox/Search-box.js"), "bottom", false)
@using Telerik.Sitefinity.Frontend.Mvc.Helpers
<h4>@Html.Resource("Display")</h4>
<div class="form-group">
<div class="radio">
<label for="rbTopLevelPages">
<input id="rbTopLevelPages" type="radio" ng-model="properties.SelectionMode.PropertyValue" value="TopLevelPages" />
@Html.Resource("TopLevelPages")
</label>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="properties.SelectionMode.PropertyValue" value="SelectedPageChildren" />
@Html.Resource("SelectedPageChildren")
</label>
<div class="label-content">
<sf-list-selector sf-page-selector
sf-selected-item-id="properties.SelectedPageId.PropertyValue"
ng-show="properties.SelectionMode.PropertyValue == 'SelectedPageChildren'"></sf-list-selector>
</div>
</div>
<div class="radio">
<label for="rbCurrentPageChildren">
<input id="rbCurrentPageChildren" type="radio" ng-model="properties.SelectionMode.PropertyValue" value="CurrentPageChildren" />
@Html.Resource("CurrentPageChildren")
</label>
</div>
<div class="radio">
<label for="rbCurrentPageSiblings">
<input id="rbCurrentPageSiblings" type="radio" ng-model="properties.SelectionMode.PropertyValue" value="CurrentPageSiblings" />
@Html.Resource("CurrentPageSiblings")
</label>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="properties.SelectionMode.PropertyValue" value="SelectedPages" />
@Html.Resource("SelectedPages")
</label>
<div class="label-content">
<sf-list-selector sf-page-selector
sf-multiselect="true"
sf-sortable="true"
sf-external-pages="multiPageSelector.externalPages"
sf-open-externals-in-new-tab="properties.OpenExternalPageInNewTab"
sf-selected-items="multiPageSelector.selectedPages"
ng-show="properties.SelectionMode.PropertyValue == 'SelectedPages'"></sf-list-selector>
</div>
</div>
</div>
<div class="form-group">
<label for="navLevelsToInclude">@Html.Resource("LevelsToInclude")</label>
<div class="row">
<div class="col-xs-3">
<select id="navLevelsToInclude" ng-model="properties.LevelsToInclude.PropertyValue" class="form-control">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="-1">All</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-xs-6">
<label for="navTemplateName">@Html.Resource("Template")</label>
<select id="navTemplateName" ng-model="properties.TemplateName.PropertyValue" class="form-control">
@foreach (var viewName in Html.GetViewNames("Navigation", @"NavigationView\.(?<viewName>\w*)$"))
{
<option value="@viewName"> @viewName</option>
}
</select>
</div>
</div>
</div>
<div class="form-group">
<label>@Html.Resource("ResponsiveDesignSectionTitle")</label>
<p class="text-muted">@Html.Raw(@Html.Resource("ResponsiveDesignDescription"))</p>
</div>
<expander expander-title='@Html.Resource("MoreOptions")'>
<style-dropdown selected-class="properties.CssClass.PropertyValue" view-name="properties.TemplateName.PropertyValue"></style-dropdown>
<div class="form-group">
<label for="navCssClass">@Html.Resource("CssClasses")</label>
<input type="text" id="navCssClass" ng-model="properties.CssClass.PropertyValue" class="form-control" />
</div>
</expander>
using System;
using System.Web;
using System.Web.Mvc;
using Ks.Sf.Web.Model.OpaHeaderController;
using ServiceStack.Text;
using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
using Telerik.Sitefinity.Frontend.Navigation.Mvc.Models;
using Telerik.Sitefinity.Frontend.Navigation.Mvc.StringResources;
using Telerik.Sitefinity.Modules.Libraries;
using Telerik.Sitefinity.Modules.Pages.Configuration;
using Telerik.Sitefinity.Mvc;
using Telerik.Sitefinity.Web;
namespace Ks.Sf.Web.Mvc.Controllers
{
[ControllerToolboxItem(Name = "OpaNavigation_MVC", Title = "OPA Header",
SectionName = ToolboxesConfig.NavigationControlsSectionName, CssClass = WidgetIconCssClass)]
[Localization(typeof (NavigationResources))]
public class OpaHeaderController : Controller
{
internal const string WidgetIconCssClass = "sfNavigationIcn sfMvcIcn";
private int? _levelsToInclude = 2;
private OpaHeaderView _model;
public string SelectedImage { get; set; }
public string SearchIndex { get; set; }
public string SearchResultsPageId { get; set; }
protected INavigationModel Model
{
get { return _model ?? (_model = InitializeModel()); }
}
public virtual int? LevelsToInclude
{
get { return _levelsToInclude; }
set { _levelsToInclude = value; }
}
public PageSelectionMode SelectionMode { get; set; }
public bool ShowParentPage { get; set; }
public string CssClass { get; set; }
public Guid SelectedPageId { get; set; }
public string SerializedSelectedPages { get; set; }
public string SerializedExternalPages { get; set; }
public bool OpenExternalPageInNewTab { get; set; }
protected override void HandleUnknownAction(string actionName)
{
Index().ExecuteResult(ControllerContext);
}
private OpaHeaderView InitializeModel()
{
var smp = SiteMapBase.GetSiteMapProvider(SiteMapBase.DefaultSiteMapProviderName);
var selectedPages = JsonSerializer.DeserializeFromString<SelectedPageModel[]>(SerializedSelectedPages);
var imageUrl = GetImageUrl();
var searchResultsPageId = GetSearchResultsUrl(smp);
var rootNodeUrl = GetRootNodeUrl(smp);
return new OpaHeaderView(SelectionMode, SelectedPageId, selectedPages, LevelsToInclude, ShowParentPage,
CssClass, OpenExternalPageInNewTab, imageUrl, SearchIndex, searchResultsPageId, rootNodeUrl);
}
private static string GetRootNodeUrl(SiteMapProvider siteMapProvider)
{
return siteMapProvider.RootNode == null
? null
: RouteHelper.ResolveUrl(siteMapProvider.RootNode.Url, UrlResolveOptions.Rooted);
}
private string GetSearchResultsUrl(SiteMapProvider siteMapProvider)
{
Guid guid;
if (!Guid.TryParse(SearchResultsPageId, out guid)) return null;
var node = siteMapProvider.FindSiteMapNodeFromKey(guid.ToString("D"));
return node == null ? null : RouteHelper.ResolveUrl(node.Url, UrlResolveOptions.Rooted);
}
private string GetImageUrl()
{
Guid imageGuid;
if (!Guid.TryParse(SelectedImage, out imageGuid)) return null;
var imageItem = LibrariesManager.GetManager().GetImage(imageGuid);
return imageItem == null ? null : imageItem.ResolveMediaUrl();
}
public ActionResult Index()
{
return View("Default", Model);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment