Quantcast
Channel: Sitefinity – Falafel Software Blog
Viewing all 73 articles
Browse latest View live

Sitefinity 8.0 has arrived at Falafel.com

$
0
0

Telerik recently released Sitefinity 8.0, adding a whole lot of great features to the already powerful CMS. Telerik Sitefinity 8.0We were so excited for Sitefinity 8.0 that the same day it came out, we offered classes on this specific release. We also upgraded our own website, Falafel.com, to 8.0 and know that you will enjoy it as much as we have been. Some of the new features of this latest Sitefinity release include improved multi-site capabilities, site-sync and taxonomy support. With this latest release also came the brand new Sitefinity Digital Experience Cloud (DEC), which will take your Marketing team’s potential to new heights. Not only that, but with the added strength of DEC, you can also harness the power of:

  • A single source of information – data connectors can pull information from all the places you need it — like Salesforce.com, Marketo, Microsoft SharePoint, the customer journey repository in Sitefinity DEC and much more — to create a single source of marketing and sales information.
  • Increased insight – through the Sitefinity DEC, you can learn more than you ever thought possible about how your customers and prospects interact with you online. Better yet, it will provide you with recommendations for moving them through the funnel for increased conversion.
  • Real-time, actionable data – discover customer and prospect trends you didn’t even know existed to enable your marketing team to target these actions via campaigns. Additionally, Sitefinity DEC provides predictions so that you can improve, personalize and optimize customer journeys.

 

When updating to Sitefinity 8.0, be aware that:

  • API changes occur between different Sitefinity versions, so be ready
  • Many manual merges might be needed to your project and web.config files
  • Depending on how complex your project is, upgrading isn’t quite as simple as one click

In addition to upgrading our own website to the latest version of Sitefinity, the certified team at Falafel has upgraded numerous companies to the latest versions of Sitefinity, and can do it for you too! Not only that, but we’re also certified on Sitefinity DEC, so we can help you get started on using this powerful marketing tool as well (more on our Sitefinity DEC services to come soon).

Whether this is your first time upgrading or you’ve done it before, you can rely on our team of Certified Sitefinity Developers to walk you through the process.

There is a wonderful benefit of having a team familiar with the update process to walk you through it.

Let us know how we can help!

The post Sitefinity 8.0 has arrived at Falafel.com appeared first on Falafel Software Blog.


Sitefinity Custom PageInboundPipe

$
0
0

Sitefinity search is very handy which is powered by Lucene. It indexes the pages content by rendering them in-memory – creates index named folder with files in App_Data\Sitefinity\Search. I recently had a requirement where we needed separate search indexes (let’s name them A and B). Index A needed to index all the pages except the pages under specific group page. And index B needed to index all those pages under that group page only.

Index A = All pages except pages under MyGroupPage.
Index B = All pages under MyGroupPage only.

Business requirement for the above scenario could be where one wants to allow registered users to search for private pages (under MyGroupPage) while others can for rest of them only.

This will require us to override PageInboundPipe to include / exclude set of pages based on the index name been processed. Let’s give index A and B a little more relevant names.

Index A = All (index named “All” will index all pages except the ones under MyGroupPage)
Index B = Member (index named “Member” will index pages under MyGroupPage only)

Overriding PageInboundPipe

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Telerik.Sitefinity;
using Telerik.Sitefinity.Configuration;
using Telerik.Sitefinity.GenericContent.Model;
using Telerik.Sitefinity.Modules.Pages;
using Telerik.Sitefinity.Pages.Model;
using Telerik.Sitefinity.Publishing;
using Telerik.Sitefinity.Publishing.Pipes;
using Telerik.Sitefinity.Services;
using Telerik.Sitefinity.Lifecycle;
using Telerik.Sitefinity.Publishing.Model;

public class CustomPageInboundPipe : PageInboundPipe
{
    private const string _memberIndex = "Member";
    private const string _memberGroupPagePath = "/my-group-page";

    public override void PushData(IList<PublishingSystemEventInfo> items)
    {
        var extranetIndexName = _memberIndex.ToLower();
        var searchIndexName = this.PipeSettings.PublishingPoint.Name.ToLower();
        var isExtranetIndex = searchIndexName == extranetIndexName;

        List<WrapperObject> wrapperObjects = new List<WrapperObject>();
        List<WrapperObject> wrapperObjects1 = new List<WrapperObject>();

        foreach (PublishingSystemEventInfo item in items)
        {
            if (item.ItemType != typeof(PageData).FullName && item.ItemType != typeof(PageNode).FullName)
            {
                continue;
            }
            WrapperObject wrapperObject = null;
            string itemAction = item.ItemAction;
            string str = itemAction;
            if (itemAction == null)
            {
                continue;
            }
            if (str == "SystemObjectDeleted")
            {
                wrapperObject = new WrapperObject(this.PipeSettings, item.Item, item.Language);
                wrapperObjects1.AddRange(new List<WrapperObject>()
                    {
                        wrapperObject
                    });
            }
            else if (str == "SystemObjectAdded")
            {
                var pageNode = this.GetPageNode(item);
                if (!this.ShouldProcessNode(pageNode, item))
                {
                    continue;
                }

                if (isExtranetIndex && IsMemberPage(pageNode))
                {
                    object[] objArray = new object[] { item };
                    wrapperObject = this.GetConvertedItemsForMapping(objArray).First<WrapperObject>();
                    wrapperObjects.AddRange(new List<WrapperObject>()
                        {
                            wrapperObject
                        });
                }
                else if (!isExtranetIndex && !IsMemberPage(pageNode))
                {
                    object[] objArray = new object[] { item };
                    wrapperObject = this.GetConvertedItemsForMapping(objArray).First<WrapperObject>();
                    wrapperObjects.AddRange(new List<WrapperObject>()
                        {
                            wrapperObject
                        });
                }
            }
            else if (str == "SystemObjectModified")
            {
                var pageNode = this.GetPageNode(item);
                if (!this.ShouldProcessNode(pageNode, item))
                {
                    continue;
                }

                object[] objArray1 = new object[] { item };
                wrapperObject = this.GetConvertedItemsForMapping(objArray1).First<WrapperObject>();
                wrapperObjects1.AddRange(new List<WrapperObject>()
                    {
                        wrapperObject
                    });

                if (isExtranetIndex && IsMemberPage(pageNode))
                {
                    wrapperObjects.AddRange(new List<WrapperObject>()
                        {
                            wrapperObject
                        });
                }
                else if (!isExtranetIndex && !IsMemberPage(pageNode))
                {
                    object[] objArray = new object[] { item };
                    wrapperObject = this.GetConvertedItemsForMapping(objArray).First<WrapperObject>();
                    wrapperObjects.AddRange(new List<WrapperObject>()
                        {
                            wrapperObject
                        });
                }
            }
        }
        if (wrapperObjects1.Count > 0)
        {
            this.PublishingPoint.RemoveItems(wrapperObjects1);
        }
        if (wrapperObjects.Count > 0)
        {
            this.PublishingPoint.AddItems(wrapperObjects);
        }
    }

    private bool ShouldProcessNode(PageNode node, PublishingSystemEventInfo item = null)
    {
        CultureInfo cultureInfo;
        if (node == null || node.NodeType == NodeType.Group || node.HasLinkedNode() || node.GetPageData(null) == null || !node.IncludeInSearchIndex || node.IsBackend)
        {
            return false;
        }
        if (item != null && item.ItemAction != "SystemObjectDeleted")
        {
            if (item.Language != null)
            {
                cultureInfo = CultureInfo.GetCultureInfo(item.Language);
            }
            else
            {
                cultureInfo = null;
            }
            if (!this.IsPagePublished(node, cultureInfo))
            {
                return false;
            }
        }
        return true;
    }

    private bool IsPagePublished(PageNode node, CultureInfo culture)
    {
        PageData pageData = node.GetPageData(culture);
        if (SystemManager.CurrentContext.AppSettings.Multilingual && culture.Equals(SystemManager.CurrentContext.AppSettings.DefaultFrontendLanguage) && pageData.PublishedTranslations.Count == 0 && pageData.Visible)
        {
            return true;
        }
        return pageData.IsPublishedInCulture(culture);
    }

    private bool IsMemberPage(PageNode node)
    {
        if (node.GetFullUrl().TrimStart('~').ToLower().StartsWith(_memberGroupPagePath.ToLower()))
        {
            return true;
        }

        return false;
    }
}

Now register this custom pipe in bootstrapped event.

Register Custom Pipe

public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        Bootstrapper.Initialized += new EventHandler<Telerik.Sitefinity.Data.ExecutedEventArgs>(Bootstrapper_Initialized);
    }

    void Bootstrapper_Initialized(object sender, Telerik.Sitefinity.Data.ExecutedEventArgs e)
    {
        if (e.CommandName == "Bootstrapped")
        {
            PublishingSystemFactory.UnregisterPipe(PageInboundPipe.PipeName);
            PublishingSystemFactory.RegisterPipe(CustomPageInboundPipe.PipeName, typeof(CustomPageInboundPipe));
        }
    }
}

Hope above helps, happy coding!

The post Sitefinity Custom PageInboundPipe appeared first on Falafel Software Blog.

Falafel Software and Mammoth Mountain named a 2014 Telerik Sitefinity Website of the Year

$
0
0

Every year Telerik, a Progress Company, honors those websites thatSitefinity Website of the Year 2014 Badge have gone above and beyond to create an innovative and unique web experience using the Telerik Sitefinity CMS. This year we are thrilled to announce that Falafel Software and Mammoth Mountain have been named as one of the 2014 Telerik Sitefinity Website of the Year award winners for the “Multi-site” category. In 2014 Mammoth Mountain came to the Falafel team to help them unify the over nine sister websites that showcase the premiere 4-season resorts large selection of amenities and services. Our team rose to the challenge and worked side-by-side with Mammoth Mountain to create a truly wonderful web experience for Mammoth Mountain’s customers.

“There is nothing that pleases us more than working with a customer to create a website that captures the essence of their brand and, in turn, gives their customers an extraordinary and revolutionary experience,” said Lino Tadros, CEO of Falafel Software.

As a Telerik Platinum Partner, it is an honor to be recognized for the excellent work our team creates using the Telerik Sitefinity CMS

Read Full Story Here

 

Learn more about our Telerik Sitefinity Consulting Services.

The post Falafel Software and Mammoth Mountain named a 2014 Telerik Sitefinity Website of the Year appeared first on Falafel Software Blog.

Sitefinity Asynchronous Search with WebAPI

$
0
0

Overview

Sitefinity CMS features a powerful internal Search Engine that allows your site visitors to search your content and find what they are looking for. The Search Engine is built on top of the .NET port of the infamous Lunene Engine which powers an unlimited number of websites today.

Unlike the solid Search Engine behind it, the Search Widget which Sitefinity offers is not as mature. It provides the options to set up a basic search page and returns the results in a form of a list. Perhaps that will be satisfactory in most cases, but what if your requirements exceed those of a simple search?. What if you’d like to create an Amazon-like search page for books or products?

It’s that sort of transition !

Game of thrones

Wait, I’ve customized Search Before

It’s possible that you have been there before, and that you have extended the Search widget with the help of great blog posts such as this one by Svetla. There is no denial that the team did a formidable job explaining how to extend and customize every part of the CMS, whether it’s through the documentation portal, blog posts, forums or several webinars. Search is still missing its asynchronous nature and there is no way that we can build our own interface for the search page while making use of a Search service which searches the appropriate index for us. Furthermore, it’s almost impossible to search Sitefinity content from another client so the idea of creating Content Driven Mobile Apps is hindered by the inadequacy of the Search Widget.

Worry not, we’ve got you covered

I put together an asynchronous implementation of the Sitefinity Search using Web API. I leveraged as much as possible the code from the Search Widget so it respects the settings of your site. It consists of one API Controller with a single action:

[HttpGet]
        public SearchResultModel Get(string searchTerm, int skip = 0, int take = 5)
        {
            var model = new SearchResultModel {Term = searchTerm};
            int hitCount;

            var results = new List<SearchDocumentModel>();
            
            Search(searchTerm, Catalogue, skip, take, out hitCount).ForEach(s => results.Add(Models.ModelFactory.Create(s)));
            
            model.Count = hitCount;
            model.Results = results;

            return model;
        }

The Search Result Model class defines the structure of the search result data:

public class SearchResultModel
    {
        public string Term { get; set; }
        public int Count { get; set; }
        public IEnumerable<SearchDocumentModel> Results { get; set; } 
    }

The repo is hosted on Github and it will be a good starting point for you if you want to put together advanced searching capabilities on your site. You will need to follow the Installation instructions on Github. Once configured, you can start hitting the end point and searching your content. You can check out the search page that’s included under “Tests”.

Conclusion

With the help of Web API, we can rewrite our search pages (using JavaScript) and asynchronously search a Sitefinity site. This will dramatically improve the search experience on your site and eventually leads to increasing your conversion rate.

 

 

The post Sitefinity Asynchronous Search with WebAPI appeared first on Falafel Software Blog.

Storing Sitefinity Sites In Source Control

$
0
0

Issues with Content Management Systems and Source Control

When developing a Sitefinity web site, you run into some challenges when it comes to properly storing your work in source control. Out of the box from its project manager, Sitefinity houses its references in the bin directory of the application so that a project build is not required to get it up and running. Being a content management system, a lot of changes occur in the site’s database. When developers want to put their Sitefinity site in source control and wish to collaborate, these sorts of issues can be difficult to deal with. This post offers a way of overcoming some of these difficulties, starting from a brand new Sitefinity site and ending on a Sitefinity site being properly committed.

This post will be using Git as the source control system, but these methods can of course be applied to any source control system you’re comfortable with.

Starting Fresh

We’re beginning with a clean slate. To create a new Sitefinity site, I’ll be using Sitefinity Project Manager. While this post uses version 8.0.5710.0 of Sitefinity, these steps can be applied to almost any version of Sitefinity. The only differences will be DLL references in your project and a few other files here and there.

NewSite

Here is “My Great Site”. I created it via Sitefinity Project Manager and ran through the setup process (specifying a license file, initial database setup, etc.) and now have a brand new functional Sitefinity site.

Initial Source Control Directory and Solution

You can create a Git repository anywhere on disk, including where the project manager created the site, but I’m going to move the files to another directory instead. Not only do I have a dedicated place for source control, but doing this also gives me assurance that I have no hidden dependencies on Sitefinity Project Manager.

MovedFiles

Note that I put the Sitefinity web project and all its files one directory deeper, into a folder called Web. Our source control solution will have other materials and we don’t want them living in the same folder as the web site, so we want to make room.

There are a few more things I like to do before making an initial commit. I want to make sure that a buildable solution for the web site is created and that no build artifacts (bin/obj files et al) are included in source control. We’ll start off by creating a solution file for the web site by opening SitefinityWebApp.csproj in Visual Studio.

Visual Studio 2013 will run an upgrade against the csproj file and generate a Migration Report. The warnings generated can be safely ignored, as they’re only saying that a few non-functional changes were made in order to make the project compatible, as well as 100+ messages indicating that the project’s files were backed up successfully. Delete the Backup folder created in /Web/. It’s a redundant set of files that we don’t need.

MigrationReport

SolutionFile

I recommend naming the solution file something other than the default (SitefinityWebApp.sln) as shown in the above screenshot, to distinguish it from other Sitefinity projects.

Ignoring Files That Do Not Belong In Source Control

In addition to the usual build artifact directories, we also don’t want to be including files that Sitefinity generates when it runs. This includes any log files, search index artifacts, and other temporary items. To accomplish this, since we’re using Git, we’re going to create a .gitignore file and place it at the root of our solution. You can start with the default C# .gitignore file that GitHub generates for you when creating a new repository, but I find it a bit too chubby for a new project and prefer to only add what’s needed.

My initial .gitignore file contains the following entries:

# User-specific files
*.suo
*.user
*.sln.docstates

# Build results
[Bb]in/
[Oo]bj/

# Sitefinity-specific folders
*/App_Data/Sitefinity/Logs/
*/App_Data/Sitefinity/Search/
*/App_Data/Sitefinity/Temp/

These include files generated for a particular user’s dev environment, the standard build artifact directories, and Sitefinity-specific directories that we don’t want to have in source control. I was explicit about particular folders within App_Data because there are other App_Data files (most notably those in /App_Data/Sitefinity/Configuration) that I do want tracked.

Making the Initial Commit

We’re now ready for our first commit. You can create a new Git repository either via the command line or via SourceTree. In SourceTree I selected “Clone / New” and selected the “Create new Repository” tab.

NewRepository

The first item we’ll want to commit is our .gitignore file. In SourceTree you’ll see a ton of files marked as Unstaged and Untracked, but .gitignore is on the top. Commit the .gitignore file. You can scroll through the file list if you wish to verify that the .gitignore file is already filtering out unwanted files.

FirstCommit

After the first commit is successful, go ahead and commit the rest of the uncommitted files as well. We’re ignoring files we don’t want committed already, and the rest are at their final destination.

Moving Sitefinity References to Libs Directory

Keeping track of Sitefinity references is very important in source control. You don’t want to push a Sitefinity site in source control only to be unable to build and run it on another machine. The bin directory, while okay for getting a site up and running from Sitefinity Project Manager, is not the place to keep mandatory references for your project. We’ll extract Sitefinity references from the bin, and move them to their own directory, then update the web project to reference these files instead of the bin. Once done we should be able to Clean and Rebuild the solution with no errors.

Copying the bin files

Open up the bin directory (/Web/bin/ in this example), and copy every file in there. In the root folder of the solution, create the folder path Libs/Sitefinity/8.0.5710.0, then paste the copied bin files into this directory. We create the Sitefinity folder inside Libs in case we have other non-Sitefinity libraries to add to the project later, and we further create a directory for the specific version of Sitefinity we’re using both for a handy reference of what Sitefinity version we’re on, as well as making the Sitefinity upgrade process a little less painful down the road.

LibsDirectory

Once the copy step is complete, you may delete the “bin” and “obj” directories inside Web/, and check SourceTree to verify that no unwanted files snuck in to source control (they’d be flagged as Deleted by SourceTree if they were). This is a quick and easy way to verify that we aren’t committing files we don’t want to be.

If you have the solution open in Visual Studio still, you will of course notice that the project’s references will nearly all be missing at this point. We need to edit SitefinityWebApp.csproj so that it’s referencing the new location.

  • Close Visual Studio (if still open)
  • Open Web/SitefinityWebApp.csproj in a text editor (for this post I’m using Notepad++)
  • Do a search for “HintPath” to find the first instance of a bin reference. I do this to verify that my Find and Replace strings are correct.
  • Do a Find and Replace: Find “<HintPath>bin\” and Replace with “<HintPath>..\Libs\Sitefinity\8.0.5710.0\”
  • For Sitefinity 8.0.5710.0, 145 occurrences were replaced.
  • Save changes and close

Once the changes are made, you can re-open the solution in Visual Studio. Expand the SitefinityWebApp project’s references and verify that no references have the little yellow warning triangle indicating that a reference is missing. Once verified, perform a Build (or a Clean and Rebuild, since we now have that power).

We’re not entirely done yet though. The Sitefinity project does not always reference every file that is originally in the bin directory. As such, when the site is run, you may get a Yellow Screen of Death saying that a certain DLL could not be found. Work through those errors, adding references to the project as needed. If a reference already exists, verify that “Copy Local” is set to true for that particular reference. Verify that you can access both the frontend and backend of your Sitefinity site. In this instance I ran into no Yellow Screens of Death, but I have in the past. It’s an easy fix, but one to be cognizant of.

Checkpoint

Taking advantage of source control is of course the main reason we’re doing this. Once we’ve built the solution and run it successfully, resolving any potential reference issues, commit. This commit will contain all the Lib references, so it’ll be a hefty one. If you’re not using a DVCS and checking in with something that requires an Internet connection (e.g. TFS), this commit could last a few moments depending on your Internet speed.

Make sure that SourceTree is not globally ignoring DLL files when you commit. When installing SourceTree initially, it asks if you want to create a global ignore. If you do, than DLL files will get ignored on commit. If this is the case, than your Libs commit will only contain the XML files from the references. If you run into this, remove your global gitignore file, or force add the files to your commit via the command “git add -f Libs/Sitefinity/8.0.5710.0/*.dll” in the Git command line.

LibsCommit

Include App_Data Files in your Project

For some reason, Sitefinity does not include its App_Data configuration files in the SitefinityWebApp project by default. This can bite you if you’re leveraging Visual Studio’s Publish capabilities to deploy your site, as the mandatory files won’t be included in the published result. Add all the files located in Web/App_Data/Sitefinity/Configuration/ to the porject (using “Show All Files” in Visual Studio to reveal the hidden directory). While here, also include your Sitefinity license file in the solution, so that it similarly does not get missed when publishing. Save all changes and commit.

As you develop and use your Sitefinity site, you may find new configuration files appearing within the Configuration directory every now and then. It’s best to occasionally check this directory inside Visual Studio (leaving “Show All Files” enabled makes this easy) to verify that you have all config files included in the project.

Commit Database Backup

One of the goals of source control is to be able to work on a project without having to acquire needed materials from any other source. Checkout the solution, build and restore, and start being productive. With that in mind, we’re going to add a backup of our empty site’s database to source control, so that developers (or yourself, if on multiple machines) can get up and running instantly. While this initial database backup will only be a few megabytes in size, it’s best to get into the habit of compressing the database backup file before committing, as database backups compress really well. I personally recommend 7zip for this, but any sort of compression will net some benefit at least. In this instance my 11 MB database was compressed down to 545 KB. That ratio is very important when your database swells to a much larger size!

At the project root directory, create a “Backups” folder, and place your backup in there. Then commit the change to source control.

Success

We’re done! We have successfully placed a Sitefinity site, and all dependencies and files, into source control. We’ve pruned out unnecessary files and directories, included a working database backup, and are now ready to push to a remote repository and checkout elsewhere.

Using the Created Repository as a Template for Future Sites

The greatest advantage of having this particular repository available is that when we need to create another Sitefinity site in the same version, we can merely clone this repository, copy the files to a different source control folder, and start development immediately! The only catch is that you have to have Sitefinity recreate the database for you, so that you don’t end up with a web site with the same name, etc. You can invoke this by simply deleting the files in App_Data/Sitefinity/Configuration. On run, Sitefinity will recreate them and run you through the first-time setup process again. Going forward you can now use your original site as a template for any future Sitefinity development you do.

The post Storing Sitefinity Sites In Source Control appeared first on Falafel Software Blog.

How to Upload Files to your Sitefinity Document Library Asynchronously

$
0
0


document-library

Sometimes, it is necessary for users of your site to be able to upload files to Document Libraries in an asynchronous manner. They could be creating user-generated content and require a space to upload a file to reference in their content, for instance. Using some JavaScript and an ASP.NET WebAPI controller, we can accomplish this fairly simply.

The HTML

The first thing we’ll need is an input that allows users to select a file. This’ll be a simple HTML input the “file” type. You can put this HTML (along with the associated JavaScript) anywhere on the page, either via Content Blocks in the backend, or via a Sitefinity widget. In my case, I’m using an MVC widget.

<input type="file" id="fileUploadInput" name="fileUploadInput" />

The JavaScript

That’s the easy part. Once we have the input in place, we need to write some JavaScript that’ll be used to listen for when a user selects a file to automatically trigger the upload. The JavaScript will watch for any changes to the input, and trigger the upload when it detects a change. We’ll be making use of jQuery’s ajax function here.

(function ($) {
    "use strict";

    var $fileUploadInput = $("#fileUploadInput");

    $fileUploadInput.on("change", function () {
        var newFile = $fileUploadInput[0].files[0];
        if (newFile === undefined || newFile === null) {
            return;
        }

        var formData = new FormData();
        formData.append("newFile", newFile);
        $.ajax({
            url: "/api/Files/Upload",
            type: "POST",
            data: formData,
            cache: false,
            contentType: false,
            processData: false
        })
        .done(function (fileUrl) {
            // Handle returned fileUrl here.
        })
        .always(function () {
            $fileUploadInput.val("");
        });
    });
})(jQuery);

There’s a fair bit going on here, so let’s break it down. First, you’ll notice that we’re wrapping our JavaScript in a self-executing anonymous function for closure (check out this blog post for an explanation). Next we grab our HTML file input and then attach a method to its “on change” event. Whenever a user selects a file, our function will get called.

Within our on-change method is where the real meat and potatoes of the JavaScript is. From the file input we grab the selected file, and validate that one exists and is present. Now that we have the file, we need to send it back to the server in order for it to be uploaded into a Document Library of Sitefinity. We will make a POST to the server to an ASP.NET WebAPI controller (detailed below).

In order to make the POST and get the selected file to the server, you have to do some special setup as opposed to just calling $.post and passing the file along as part of the POST parameters. On line 12 we create a new FormData object and append our file to it. This is the data that we send via the $.ajax call (line 17) to contact the server. We specify the URL to the WebAPI controller method, and set cache, contentType, and processData all to false.

Once all that is set up, you can create any number of Promises to suit your needs. In this example, I have two: done() and always(). In the done() method, it accepts the URL to the file as a parameter (which is what our controller returns), and that can be used to provide the user who uploaded the file with a link to copy/paste or use as they see fit.

The Controller

Initial Setup

Finally we have our controller. This not only provides a means for our asynchronous JavaScript method to contact the server, but also handles the process of creating a new Document item and uploading it to a specific Document Library in Sitefinity.

  • In the root of your web project (likely called SitefinityWebApp if it hasn’t been renamed), add an “Api” folder
  • Within Api, create a new file: FilesController.cs

A Note on Security

An important consideration when creating public-facing API methods is handling who is allowed to use them. Since this is a method that anyone can post a file to, the method itself is responsible for handling security. In this case, the only security rule is “the uploading user must be logged into Sitefinity.” Your application’s security concerns can vary greatly, and be far more complex than what this simple scenario entails.

Code

With that in mind, here is the full code for FilesController.cs We’ll break it down right after the snippet block.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Http;
using Telerik.Sitefinity.GenericContent.Model;
using Telerik.Sitefinity.Libraries.Model;
using Telerik.Sitefinity.Modules.Libraries;
using Telerik.Sitefinity.Security;
using Telerik.Sitefinity.Security.Claims;
using Telerik.Sitefinity.Security.Model;

namespace SitefinityWebApp.Api
{
    public class FilesController : ApiController
    {
        private readonly LibrariesManager _librariesManager = LibrariesManager.GetManager();

        [HttpPost]
        public string Upload()
        {
            var currentUserId = ClaimsManager.GetCurrentUserId();
            if (currentUserId == Guid.Empty)
            {
                throw new UnauthorizedAccessException();
            }
            User currentUser = UserManager.GetManager().GetUser(currentUserId);
            
            HttpFileCollection files = HttpContext.Current.Request.Files;
            if (files.Count <= 0)
            {
                throw new InvalidOperationException("No file was detected to upload.");
            }
            
            HttpPostedFile newFile = files[0];

            DocumentLibrary documentLibrary = this.GetDocumentLibrary();
            string fileExtension = Path.GetExtension(newFile.FileName);
            string fileName = files[0].FileName;
            string urlName = this.GetUniqueUrlName(documentLibrary, fileName);

            Document document = _librariesManager.CreateDocument();
            document.Author = currentUser.UserName;
            document.DateCreated = DateTime.UtcNow;
            document.Owner = currentUser.Id;
            document.Parent = documentLibrary;
            document.Title = urlName;
            document.UrlName = urlName;

            _librariesManager.Upload(document, newFile.InputStream, fileExtension);
            _librariesManager.RecompileItemUrls(document);
            _librariesManager.Lifecycle.Publish(document);
            document.ApprovalWorkflowState = "Published";
            _librariesManager.SaveChanges();

            Document liveDocument = _librariesManager.Lifecycle.GetLive(document) as Document;
            if (liveDocument == null)
            {
                throw new InvalidOperationException("Document could not be uploaded properly.");
            }
            return liveDocument.MediaUrl;
        }

        private static string GetUniqueUrlName(DocumentLibrary documentLibrary, string fileName)
        {
            List<string> existingUrlNames = documentLibrary.ChildContentItems
                .Where(c => c.Status == ContentLifecycleStatus.Live && c.Visible)
                .Select(c => c.UrlName.ToString()).ToList();
            string safeTitle = fileName.GetSafeUrlName(); 
            while (existingUrlNames.Contains(safeTitle))
            {
                safeTitle += "-";
            }
            return safeTitle;
        }

        private DocumentLibrary GetDocumentLibrary()
        {
            const string documentLibraryName = "My Document Library";
            DocumentLibrary documentLibrary = _librariesManager.GetDocumentLibraries()
                .FirstOrDefault(l => l.Title == documentLibraryName);
            if (documentLibrary == null)
            {
                throw new NotImplementedException
                    (string.Format("The Document Library \"{0}\" does not exist to upload new files in.", documentLibraryName));
            }
            return documentLibrary;
        }
    }
}

Our asynchronous call starts at the Upload() method. Our class inherits from ApiController and has decorated Upload() with the [HttpPost] attribute. This enables the JavaScript asynchronous $.ajax() call to get into this method with the provided file. The start of the method simply validates that a file was uploaded and that the user performing the upload is logged in as an existing Sitefinity user.

As you can see, our method has no input parameters. The file we uploaded instead arrives within the HttpContext.Current.Request.Files collection (line 30). Starting at line 38 we dig into Sitefinity’s code to create a Document. Here, we fetch the DocumentLibrary that we want to store our file in. In this example we’re grabbing a library by name and getting the same one every time (all frontend users upload to the same DocumentLibrary).

Once we have that we get the file details (name, extension, url name) and begin creating the Document. We set up the current user as the Author/Owner and assign values to other basic fields as well. In addition, the property on Document that you assign the DocumentLibrary to is called “Parent” (line 47). You assign it the DocumentLibrary object itself, not just the ID. Once our Document’s properties are set, we go through the process of actually uploading the file to Sitefinity and publishing the Document so that we have a Live version to work with (lines 51-55).

Finally, we retrieve the Live version of our just-published Document and return its URL. This is the “fileUrl” that our done() Promise method retrieves in the JavaScript. You can, of course, alter the Upload() controller method to return whatever you like, but for our scenario here we simply return the URL.

That’s It!

With our HTML, JavaScript, and C# in place, our asynchronous file upload feature is ready to rock. Users can now select a file via the HTML input, and then get a valid URL as a response, with their file now fully uploaded and ready to use. In my next post I will show you how to use this feature to extend the functionality of a Kendo Editor to allow dynamic insertion of file URLs on the fly.

The post How to Upload Files to your Sitefinity Document Library Asynchronously appeared first on Falafel Software Blog.

Retrieving Simple Custom Properties from Sitefinity Content Items

$
0
0

Sitefinity offers many extension points to its core content types, as well as letting you create completely customized content types of your own via the Module Builder. You can add custom fields to existing types like News and Events, or add as many fields as you like to custom content types. Working with these custom properties in code can seem a little daunting at first: How do you do it? What data types do you work with? Which methods do you call on your objects? There are several ways of accessing certain kinds of properties, while others you have to know the type of object you’re getting or else you’ll get errors at runtime. This post will show you how to access a couple of the most common types of custom properties, which work both for custom fields on built-in types and fields for custom content types created through the Module Builder. It will also touch upon how to discover unknown types in code without needing prior knowledge of said custom fields (other than their names).

Defining Custom Properties in Sitefinity

I won’t delve too deeply in how to set up custom properties in Sitefinity since that’s beyond the scope of this post. On most of the Content pages (Content Blocks, News, Events, etc.) you can find a “Custom Fields for {type of content}” link. This will take you to a screen where you can define new custom properties, specifying name, type, whether or not they’re required, and other details. When creating new types from the Module Builder the interface is very much the same. This post will assume this step has already been taken, and will explain the next step: accessing these custom fields in code.

Testing the Waters Before Taking the Plunge

If you are writing generic code that fetches custom properties (to perform generic validation, for example) and you won’t know what the property name is until runtime, you can use the method DoesFieldExist() to validate the given property name. If you call myContentItem.GetValue(“PropertyThatDoesNotExist”), you’ll get a runtime exception. Instead, call myContentItem.DoesFieldExist(“PropertyThatDoesNotExist”), which returns false. From there you can program around the case of being given a non-existing field. This can also be used in scenarios where you have multiple environments, where one has a new field and another doesn’t have it yet. With DoesFieldExist() you can safely transition your code base without having to maintain separate versions.

String Properties

Now we can move on to working with known, existing properties. The most common type of property I find myself using is of type string. Their use ranges from simple short fields like a name, to large chunks of data containing HTML. Whether you’re working with Short Text or Long Text custom properties, the method of accessing them is the same, as at the end of the day they are both considered to be of the same type.

However, while the type stored in the database is a string, Sitefinity treats it as an “Lstring.” Lstring, or Localizable String, is the type Sitefnity treats custom string fields as, since it’s what Sitefinity uses to maintain a value in multiple languages. When working with simple strings you typically just want the main string value (especially if you’re not concerned with multilingual values at this point), and luckily Sitefinity implemented Lstring so that it implicitly converts to a string. This means that the following code snippet:

Lstring myLstring = new Lstring();
string myRegularString = myLstring;

is legal code that can run without errors at compile-time or runtime.

With that in mind, there are two primary methods of fetching string properties. The first uses the same method used for fetching most properties: myContentItem.GetValue<string>(“PropertyName”). This returns a “string” type object and ignores Lstring completely. The second is myContentItem.GetString(“PropertyName”). This function returns an Lstring, but as shown above can be immediately assigned to a string variable.

When to Use GetValue<string>() vs. GetString()

At first glance, you’d think that the two methods are interchangeable. However, there are circumstances where one method will cause an error while the other doesn’t, and other circumstances where the opposite is true. This issue primarily arises due to the previously-mentioned implicit conversation that Lstring has with string. While the compiler will always accept this, at runtime issues can arise.

Use GetValue<string>() for Queries

Consider the following: We have a “PressRelease” custom content type, with, among other custom properties, a string property “Title.” We have a PressRelease with title “Foo” that we wish to fetch from Sitefinity, so we write the following code:

Type pressReleaseType = TypeResolutionService
    .ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.PressRelease");
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager();

DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType)
    .FirstOrDefault(d => d.GetString("Title") == "Foo");

This code compiles and looks correct: Compare the Title string field with the title we want to fetch, and return the result. But when we try to run it, however:

ErrorUsingGetStringInQuery

When querying using custom properties, you have to use GetValue() instead:

DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType)
    .FirstOrDefault(d => d.GetValue<string>("Title") == "Foo");

Note that this seems to be the only expression that Sitefinity will accept. I tested out many other expressions, and all yielded different runtime exceptions:

  • d.GetValue<Lstring>(“Title”) == “Foo”
  • d.GetValue<Lstring>(“Title”).ToString() == “Foo”
  • d.GetValue<Lstring>(“Title”) == new Lstring(“Foo”)
  • d.GetValue(“Title”).ToString() == “Foo”

Use GetString() for Retrieving Values

After you have your DynamicContent object retrieved, you’ll want to get the actual properties from it. In order to fetch the title, one would think to use the same method as they did in the FirstOrDefault() query:

string title = pressRelease.GetValue<string>("Title");

But then you’re greeted with another runtime error:

ErrorUsingGetValueToFetchProperty

You have to use GetString() here instead in order to fetch the property without throwing any exceptions:

// GetString() returns Lstring, which implicitly converts to string in this statement.
string title = pressRelease.GetString("Title");

In both of the instances where an exception was thrown, the implicit conversion from Lstring to string cannot take place. In the first, the query is being passed back to SQL Server (or whatever your backing data store happens to be), which removes C# from the equation and leaves it unable to do the conversion. In the second, the method GetValue() doesn’t use implicit conversion, since not every instance of T will be implicitly convertible from the object given.

String Custom Properties Summary

Here’s a complete simple example of all of the correctly-working code snippets from above, from querying custom properties to accessing their data. We also add in an additional Where() omitted above for brevity that assures we’re getting the Live version of the content item. Remember: GetValue(“PropertyName”) in your LINQ queries, and GetString(“PropertyName”) when retrieving the values.

Type pressReleaseType = TypeResolutionService
    .ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.PressRelease");
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager();

DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType)
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible)
    .FirstOrDefault(d => d.GetValue<string>("Title") == "Foo");
string title = pressRelease.GetString("Title");

“Number” Custom Properties

When specifying a numeric type for custom properties there are a couple primary considerations: Do we allow fractional numbers, and is the number required. In these basic cases that leaves us with up to four possible combinations. Regardless of the options selected, however, Sitefinity returns these properties as the same type in C#. Sitefinity will always return the decimal type for custom properties that are numbers.

It should be noted that the limitations prescribed to the custom properties, the backing data store and code is unaffected. The limitations/requirements are all enforced on the Sitefinity site itself. This means that in code you have to be careful. If you attempt to get a value of type “decimal” instead of “decimal?” and there’s no value, you’ll get a runtime exception. If the value is required, however, you can generally get away with not using the nullable decimal type, simplifying your code.

In the following example we’ve added a few custom properties to our custom content type. SubscriberCount and NumberOfWidgets represent whole numbers, while Price and Discount represent fractional. Note the use of decimal? vs. decimal:

decimal subscriberCount = pressRelease.GetValue<decimal>("SubscriberCount"); // Required, whole numbers only
decimal price = pressRelease.GetValue<decimal>("Price"); // Required, limited to 2 decimal places
decimal? numberOfWidgets = pressRelease.GetValue<decimal?>("NumberOfWidgets"); // Not required, whole numbers only
decimal? discount = pressRelease.GetValue<decimal?>("Discount"); // Not required, limited to 2 decimal places.

Querying Numeric Custom Properties

While there is no equivalent built-in method to “GetString()” for numeric types, there is still a small distinction made between querying for numeric custom properties and fetching them. When querying by numeric properties you must use the nullable type even if the value can never be null. If you write the following, you will get an exception:

DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType)
    .FirstOrDefault(d => d.GetValue<decimal>("Price") > 2.50m);

numeric-type-query-error

To query your content items by a custom numeric property, use the nullable deicmal type:

DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType)
    .FirstOrDefault(d => d.GetValue<decimal?>("Price") > 2.50m);

Determining Types of Unknown Custom Properties

Sitefinity’s GetValue() method does not require a type parameter in order to function when retrieving values. When no type is specified, it will return your value in the form of System.Object. We can use this to our advantage when we’re not aware of what data type our custom property is and want to know exactly what type to use. Now most of the time, you won’t need to do this. After reading this post you are already aware of the most two common types (strings and decimals). But if you’re ever in a scenario where the type is unknown, and you don’t want to go through several build -> run -> fail cycles, debugging and simply getting the object and inspecting the type can be much quicker.

Other Custom Property types

There are several other kinds of properties you can create, ranging from Classifications (i.e. Tags and Categories) to Related Media (i.e. Images, Videos, Documents). Accessing these fields are more complex than reading the right property using the right type, and will be shown in a future blog post. Stay tuned!

The post Retrieving Simple Custom Properties from Sitefinity Content Items appeared first on Falafel Software Blog.

Quick Links to Sitefinity Resources

$
0
0

Whether you’re setting up your very first Sitefinity website or upgrading to the latest version, at some point you’re probably going to need to hunt down one or more of these items. Hopefully this post can serve as a quick guide (or reminder!) to where you can find these important Sitefinity Resources.

Note: Things change so if any of this becomes out of date please let me know and I will update it as time goes on!

Sitefinity Project Manager

Direct Link: http://www.telerik.com/account/your-products/download-list.aspx?skucid=21

The Sitefinity Project Manager is the tool used to create new Sitefinity sites, as well as performing upgrades. It’s available as both an installed and stand-alone application.

Latest version

After logging into your account, the direct link above will take you to the download page linking to the current latest version of Sitefinity.

Sitefinity-Downloads-Page-Latest

Clicking the blue download button will download the zipped version of the project manager for the version shown in the gray box, which should always be the very latest official released version.

You can also get to this page after logging in by clicking Products and Subscriptions and selecting Sitefinity from the list:

Sitefinity-Products-Link

This will take you to your product page for Sitefinity, which lists all the domains for which you have purchased licenses:

Sitefinity-Product-Page-Download

From here you can click the blue download button on the right to be taken to the main download page for Sitefinity to download the project manager or other related files.

All Product Files

By clicking the “Browse all product files” link in the main download page above you can find additional downloads related to the latest release.

Sitefinity-Downloads-Page

  • Release notes – Contain the full details of the latest release, including fixes and breaking changes
  • Evaluation Edition – Installer for the project manager, useful for first-time users to try out the product quickly
  • Project Manager – The same file served by the Download button on the main page
  • STS Web App – Companion web application used to add external authentication and Single Sign On (SSO) to Sitefinity
  • Sitefinity PDB files – debug file symbols useful for troubleshooting

Internal builds for the latest release are also available here. These are generally “hotfix” releases that address a specific problem found after the main release.

You can also find the full list of internal builds for the latest release by clicking the “Latest Internal Builds” link on the main download page for Sitefinity.

Finally, you can track the history and availability of internal builds on the official Sitefinity forum: http://www.sitefinity.com/developer-network/forums/internal-builds

Previous versions

Direct Link: http://www.telerik.com/account/your-products/product-versions/olderversions.aspx?product=SF4.0

Older versions of Sitefinity can be downloaded by clicking the “Older versions” link on the main download page above, which will take you to the full history of Sitefinity releases including all release notes.

Sitefinity-Version-History

Selecting a specific version will take you to the list of downloads specific for that version, including the full installer, project manager, STS web app, and internal builds associated with that release.

If you are looking for a specific internal build, select the version that is closest to, but not higher than, the build you’re seeking.

For example, if you needed the internal build project manager for version 7.2.5324, you would first select the official release for 7.3.5200. The page for that version will then have the desired version listed under Internal Builds

Sitefinity License Files

Direct Link: http://www.telerik.com/account/license-management.aspx

The link above will take you to the list of sites for which you have purchased licenses, as well as any trial or developer licenses associated with your account.

Sitefinity-License-Management

If the link above changes or no longer works, you can access this page after by clicking Products and Subscriptions and selecting Sitefinity from the list:

Sitefinity-Products-Link

Then on the sidebar of the product page, click the “Manage Domains and Licenses” link:

 Sitefinity-Product-Page-Licenses

Select the site for which you require a license and click the “Manage” link to see the details page for that site.

Sitefinity-Domain-Licenses-Page

To download the actual licenses, click the “Show Available Keys” link on the right side of the page, revealing the full list of available license files.

Sitefinity-License-Versions

Make sure that the version you are running exactly matches the license file you are downloading and click the “Download” link to save the file.

Developer SDK

Direct Link: https://github.com/Sitefinity-SDK

Another important resource for Sitefinity is the developer kit (SDK) which contains several samples and helpful resources for developers, including the Widget templates for the latest release.

Widget Templates

Direct Link: https://github.com/Sitefinity-SDK/SitefinityResources/blob/master/Templates/WidgetTemplates.zip

Probably the most useful and frequent resource you’ll need are the Sitefinity widget templates, which are the layout files for all of the default Sitefinity widgets, such as Content Block, News Widget, Search Box, Navigation Menu, and all of the templateable controls within Sitefinity.

The zip file contains a copy of the default contents of every template which you can extract and use as a base for customization.

Additional Sitefinity resources such as the default Themes and Workflows as well as the API References for Visual Studio Help Viewer are available here: https://github.com/Sitefinity-SDK/SitefinityResources

Sitefinity Thunder

Direct Link: https://visualstudiogallery.msdn.microsoft.com/381fde64-9898-4a5d-ae20-861e119bbf92

Another helpful resource for developers is the Thunder plugin for Visual Studio. This helpful plugin (also installable directly within Visual Studio) adds several new item templates for Visual Studio as well as adding connectivity to your sites to edit content directly from Visual Studio.

More info on Thunder is available here: http://docs.sitefinity.com/sitefinity-thunder-automate-you-development

Sitefinity Documentation

Direct Link: http://docs.sitefinity.com/

The last but certainly not least resource you’ll want to bookmark is the documentation. This extensive site documents all the features of Sitefinity including code samples for developers and is the first place you should look whenever you have Sitefinity questions.

Sitefinity Resources: Wrapping Up

This post links directly to many of the frequently-used resources for Sitefinity to make it easier to find what you need, and will be updated if these links change or break. Please report any missing or broken links via the comments, and as always I hope this was helpful!

The post Quick Links to Sitefinity Resources appeared first on Falafel Software Blog.


Real-Time Visitor Count in Sitefinity with SignalR

$
0
0

In the previous post we looked at how to install and use SignalR in Sitefinity with a really simple popup notification sample. This time we’ll look at how we can leverage SignalR to create a real-time count of readers visiting a blog post. Be sure you’ve reviewed the previous post as we’ll be extending the existing Notify Hub and JavaScript for this example.

Modifying the Hub Class

The first thing we need to do is add a static int property to count the visitors to the page. We also need to expose a public method to trigger the Hub so that it can push out the update to all connected clients (not just the latest visitor).

The actual counting of visitors will be achieved by overriding the OnConnected and OnDisconnected methods to that the total is incremented or decremented respectively as clients connect (load the page) and disconnect (close their browser or navigate away from the page).

The complete code sample is below:

using System;
using System.Linq;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs; 

namespace SitefinityWebApp.App_Custom.SignalrHubs
{
[
HubName(“notice”)] public class NoticeHub : Hub
{
private static int visitorCount = 0; 

public override System.Threading.Tasks.Task OnConnected()
{
visitorCount++;
RefreshCount();
return base.OnConnected();
}
 

public override System.Threading.Tasks.Task OnDisconnected()
{
visitorCount–;
RefreshCount();
return base.OnDisconnected();
}
 

public void RefreshCount()
{
var visitorText = string.Format(“{0} user{1} currently reading this post.”, visitorCount, visitorCount == 1 ? “” : “s”);
Clients.All.refreshVisitorCount(visitorText);
}
}
}

 

Update the Blog Details Template

Because we already added all the JavaScript and CSS references last time, we simply need to update the Sitefinity Blog Details template with an area to display the viewer count text, as well as the script to connect to the hub.

Ideally, you’d probably want to put this in a separate file or part of your JavaScript framework, but to keep things simple I’ll just put it inline. Here’s a copy of the updated Blog Details template.

<%@ Control Language=”C#” %>
<%
@ Register TagPrefix=”telerik” Namespace=”Telerik.Web.UI” Assembly=”Telerik.Web.UI” %>
<%
@ Register TagPrefix=”sf” Namespace=”Telerik.Sitefinity.Web.UI” Assembly=”Telerik.Sitefinity” %>
<%
@ Register TagPrefix=”sf” Namespace=”Telerik.Sitefinity.Web.UI.ContentUI” Assembly=”Telerik.Sitefinity” %>
<%
@ Register TagPrefix=”sf” Namespace=”Telerik.Sitefinity.Web.UI.PublicControls.BrowseAndEdit”
Assembly=”Telerik.Sitefinity” %>
<%
@ Register Assembly=”Telerik.Sitefinity” Namespace=”Telerik.Sitefinity.Modules.Comments.Web.UI.Frontend” TagPrefix=”comments” %>
<%
@ Import Namespace=”Telerik.Sitefinity” %>
<%
@ Import Namespace=”Telerik.Sitefinity.Web.UI” %>
<%
@ Import Namespace=”Telerik.Sitefinity.Modules.Comments” %>
<script type=”text/javascript”>

$.connection.hub.logging = true;
$.connection.hub.start();

$.connection.notice.client.refreshVisitorCount = function onRefreshCount(msg) {
$(
“.count”).text(msg);
};
</script>

<sf:SitefinityLabel id=”title” runat=”server” WrapperTagName=”div” HideIfNoText=”true” HideIfNoTextMode=”Server” />
<
telerik:RadListView ID=”SingleItemContainer” ItemPlaceholderID=”ItemContainer” AllowPaging=”False”
runat=”server” EnableEmbeddedSkins=”false” EnableEmbeddedBaseStylesheet=”false”>
<
layouttemplate>
<%— <div class=”sfpostLinksWrp”>
<sf:MasterViewHyperLink class=”sfpostBack sfback” Text=”<%$ Resources:BlogResources, allposts %>” runat=”server” />
</div> —
%>
<asp:PlaceHolder ID=”ItemContainer” runat=”server” />
</
layouttemplate>
<
itemtemplate>
<
div class=”sfpostDetails sfdetails” data-sf-provider=’<%# Eval(“Provider.Name”)%>data-sf-id=’<%# Eval(“Id”)%>data-sf-type=”Telerik.Sitefinity.Blogs.Model.BlogPost”>
<
sf:FieldListView ID=”PostTitle” runat=”server”
Text=”{0}” Properties=”Title”
WrapperTagName=”h1″ WrapperTagCssClass=”sfpostTitle sftitle” EditableFieldType=”ShortText”
/>
<
div class=”sfpostAuthorAndDate sfmetainfo”>
<
asp:Literal ID=”Literal2″ Text=”<%$ Resources:Labels, By %>runat=”server” />
<
sf:PersonProfileView ID=”PersonProfileView1″ runat=”server” />
<
sf:FieldListView ID=”PostDate” runat=”server”
Format=” | {PublicationDate.ToLocal():MMM dd, yyyy}”
/>
<
comments:CommentsCountControl ID=”CommentsCountControl1″ runat=”server”
ThreadKey=’<%# ControlUtilities.GetLocalizedKey(Eval(“Id”), null, CommentsBehaviorUtilities.GetLocalizedKeySuffix(Container.DataItem.GetType().FullName)) %>
AllowComments=’<%# Eval(“AllowComments”) %>
ThreadType=’<%# Container.DataItem.GetType().FullName %>
NavigateUrl=”#commentsWidget”
DisplayMode=”ShortText”/>
| <div id=”blogCounter”>Active Readers: <span class=”count” />

<comments:CommentsAverageRatingControl ID=”CommentsAverageRatingControl1″ runat=”server”
ThreadKey=’<%# ControlUtilities.GetLocalizedKey(Eval(“Id”), null, CommentsBehaviorUtilities.GetLocalizedKeySuffix(Container.DataItem.GetType().FullName)) %>
ThreadType=’<%# Container.DataItem.GetType().FullName %>
NavigateUrl=”#commentsWidget”
DisplayMode=”FullText”/>
</
div>
<
sf:FieldListView ID=”PostContent” runat=”server”
Text=”{0}” Properties=”Content”
WrapperTagName=”div” WrapperTagCssClass=”sfpostContent sfcontent” EditableFieldType=”LongText”
/>

<asp:PlaceHolder ID=”socialOptionsContainer” runat=”server”>
</
asp:PlaceHolder>
<
comments:CommentsWidget ID=”CommentsWidget1″ runat=”server”
ThreadKey=’<%# ControlUtilities.GetLocalizedKey(Eval(“Id”), null, CommentsBehaviorUtilities.GetLocalizedKeySuffix(Container.DataItem.GetType().FullName)) %>
AllowComments=’<%# Eval(“AllowComments”) %>ThreadTitle=’<%# Eval(“Title”) %>ThreadType=’<%# Container.DataItem.GetType().FullName %>
GroupKey=’<%# ControlUtilities.GetUniqueProviderKey(“Telerik.Sitefinity.Modules.Blogs.BlogsManager”, Eval(“Provider.Name”).ToString()) %>
DataSource=’<%# Eval(“Provider.Name”)%>‘ />
</
div>
</
itemtemplate>
</
telerik:RadListView>

That’s all there is to it! Loading the page initially fires the event on connect to trigger the count, which in turn pushes out that message to all connected clients.

As users come and go, the count is automatically updated in real time to all visitors on the page. Here are some screenshots of it in action.

Sitefinity-Visitor-Count-With-SignalR Sitefinity-Visitor-Count-With-SignalR-Increase-Count Sitefinity-Visitor-Count-With-SignalR-Decrease-Count

Wrapping Up

Thanks to SignalR we were able to add new, interactive functionality to our blog with just a few lines of code. So much can be done with this library, so I encourage you to try it out for yourself. I’m also very interested to hear if and how you are making use of SignalR, so please sound off about your projects in the comments!

As always, I hope this was helpful!

The post Real-Time Visitor Count in Sitefinity with SignalR appeared first on Falafel Software Blog.

Falafel Software Furthers Commitment to Sitefinity Developers with New Software Development Kit

$
0
0

CAPITOLA, CA – July 15, 2014 – Falafel Software, the leading provider of software development, consultation and training services has released a new software development kit for Sitefinity. The software development kit (SDK), Babaganoush, instantly adds functionality to Sitefinity websites.

The Babaganoush SDK allows developers to start coding on Sitefinity instantly to create server, client or mobile apps — allowing them to focus on solutions instead of troubleshooting issues or inconsistencies. With the SDK they are able to dramatically cut development time and costs by following proven patterns that provide short cuts.

The code in Babaganoush has been created in response to real demands from enterprise companies and has been proven in highly scalable Sitefinity deployments. Numerous Falafel Software clients had approached the Sitefinity Platinum Partner for assistance with internal Sitefinity projects. 

Babaganoush is the culmination of many years of hard work as leaders in the Sitefinity space. The most unique aspect of Babaganoush is that it has been manufactured and tested out in the wild.

General Availability
Babaganouhs is immediately available for download at http://babaganoush.falafel.com/download

About Falafel Software
Falafel Software Inc., an 8-time Microsoft Gold Certified partner, has been providing custom software development, consultation, and training services worldwide since 2003. Our team of Microsoft Certified Professionals will exceed your expectations when you choose any of our services. Whether you need help moving to the cloud, a suite of mobile applications, or assistance with any of our partner’s technology, the Falafel family is ready to help. Contact us to discuss your technology needs.

The post Falafel Software Furthers Commitment to Sitefinity Developers with New Software Development Kit appeared first on Falafel Software Blog.

Programmatically add fields to Pages module in Sitefinity 6.3 and 7

$
0
0

Sitefinity 7 comes with a very cool new feature that enables us to add custom fields to Pages module through UI. Although this feature was previously not available in version 6.3, but its effect could be achieved by using Page Attributes. Also, in version 7, its called Custom Fields. Following is the helper class.

ModuleHelper.cs

public class ModuleHelper
{
    public static void RegisterGuidArrayFieldSelectorForPages<C>(string fieldName) where C : FieldControlDefinitionElement
    {
        RegisterPageNodeDynamicData();

        string itemType = typeof(PageNode).FullName;
        var fieldExists = GetMetaFieldsForType(itemType).Where(f => f.FieldName == fieldName).SingleOrDefault() != null;

        if (!fieldExists)
        {
            var metaManager = MetadataManager.GetManager(); 
            var field = metaManager.CreateMetafield(fieldName); 
            field.FieldName = fieldName; 
            field.Title = fieldName; field.Hidden = false;
            field.ClrType = typeof(System.Guid[]).FullName; 
            var metaType2 = metaManager.GetMetaType(typeof(PageNode));
            metaType2.Fields.Add(field); 
            metaManager.SaveChanges();

            var manager = ConfigManager.GetManager(); 
            manager.Provider.SuppressSecurityChecks = true;

            var section = manager.GetSection<ContentViewConfig>();
            var backendSection = section.ContentViewControls[PagesDefinitions.BackendPagesDefinitionName];
            var views = backendSection.ViewsConfig.Values.Where(v => v.ViewType == typeof(DetailFormView));
            foreach (DetailFormViewElement view in views)
            {
                var sectionToInsert = CustomFieldsContext.GetSection(view, CustomFieldsContext.customFieldsSectionName, itemType);

                var fieldConfigElementType = TypeResolutionService.ResolveType(typeof(C).FullName);
                C newElement;
                newElement = Activator.CreateInstance(fieldConfigElementType, new object[] { sectionToInsert.Fields }) as C;

                newElement.DataFieldName = "CustomFields." + fieldName;
                newElement.FieldName = fieldName;
                newElement.Title = fieldName;
                newElement.DisplayMode = FieldDisplayMode.Write;
                newElement.Hidden = false;

                sectionToInsert.Fields.Add(newElement);
            }

            var frontendSection = section.ContentViewControls[PagesDefinitions.FrontendPagesDefinitionName];
            views = frontendSection.ViewsConfig.Values.Where(v => v.ViewType == typeof(DetailFormView));
            foreach (DetailFormViewElement view in views)
            {
                var sectionToInsert = CustomFieldsContext.GetSection(view, CustomFieldsContext.customFieldsSectionName, itemType);
                var fieldConfigElementType = TypeResolutionService.ResolveType(typeof(C).FullName);
                C newElement;
                newElement = Activator.CreateInstance(fieldConfigElementType, new object[] { sectionToInsert.Fields }) as C;

                newElement.DataFieldName = "CustomFields." + fieldName;
                newElement.FieldName = fieldName;
                newElement.Title = fieldName;
                newElement.DisplayMode = FieldDisplayMode.Write;
                newElement.Hidden = false;

                sectionToInsert.Fields.Add(newElement);
            }

            manager.SaveSection(section);
            manager.Provider.SuppressSecurityChecks = false;

            RestartApplication();
        }
    }

    private static void RegisterPageNodeDynamicData()
    {
        var pageNodeType = App.WorkWith().DynamicData().Types().Where(s => s.ClassName == "PageNode").Get().FirstOrDefault();
        if (pageNodeType != null) 
            return;

        App.WorkWith()
            .DynamicData()
            .Type()
            .CreateNew("PageNode", "Telerik.Sitefinity.Pages.Model")
            .Do(dt => dt.DatabaseInheritance = DatabaseInheritanceType.vertical)
            .SaveChanges(true);
    }

    private static IList<MetaField> GetMetaFieldsForType(string type)
    {
        var existingType = TypeResolutionService.ResolveType(type); 
        var existingClassName = existingType.Name;
        var existingNamespace = existingType.Namespace; 
        var mgr = MetadataManager.GetManager();

        var types = mgr.GetMetaTypes().FirstOrDefault(dt => dt.ClassName == existingClassName && dt.Namespace == existingNamespace);
        var fields = types != null ? types.Fields : new List<MetaField>();
            
        return fields;
    }

    private static void RestartApplication()
    {
        if (SystemManager.Initializing) 
            return;

        SystemManager.RestartApplication(true, true);
    }
}

 

Usage

Add new field on Bootstrapped event.

protected static void OnBootstrapperInitialized(object sender, ExecutedEventArgs e)
{
    if (e.CommandName == "Bootstrapped")
    {
        ModuleHelper.RegisterGuidArrayFieldSelectorForPages<ControlToUse>("YOUR_FIELD");
    }
}

 

Where ControlToUse is a Field Control which inherits FieldControlDefinitionElement and implements IRolesFieldSelectorDefinition. Happy Coding! And want to learn more? Register now for FalafelCon 2014 and perfect your skills, expand your horizons, and get inspired.

The post Programmatically add fields to Pages module in Sitefinity 6.3 and 7 appeared first on Falafel Software Blog.

Installing Sitefinity Thunder into Visual Studio 2015

$
0
0

If you have tried to install Sitefinity Thunder (v 1.5.0.1) from the Nuget package manager inside of Visual Studio 2015, you might have gotten the error below that “the extension is not supported on the current product”.  I am running Visual Studio 2015 Enterprise and I got the error on multiple machines.

ThunderErrorVS15

Don’t panic!  Close down Visual Studio, head on over to Visual Studio Gallery and download the extension from there and install it. https://visualstudiogallery.msdn.microsoft.com/381fde64-9898-4a5d-ae20-861e119bbf92

Everything will work just fine!

Cheers

Lino

The post Installing Sitefinity Thunder into Visual Studio 2015 appeared first on Falafel Software Blog.

Complex Custom Properties for Sitefinity Content

$
0
0

In my previous post I talked about basic access of custom properties in Sitefinity. Namely, strings and numbers. Those alone cover a lot of what you’d use when it comes to custom fields on built-in content types and custom content types. In this post, however, we’re delving into some of the more complex custom properties that can be associated with Sitefinity content. Like in the previous post, we’ll cover both how to query by a complex property (where applicable) and how to fetch the content of the complex property.

Classifications (Tags, Categories, Custom)

Fetching

Tags and Categories within Sitefinity are both different kinds of Taxonomies, and thus they are both accessed in a similar fashion. Let’s say our Press Release custom content type has a “Tags” property, and we have several Tags defined under Content > Tags: “A”, “B”, and “C”. Whenever fetching a content item’s Tags or querying/filtering by Tags, we will be working with the aforementioned stored IDs.

DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType)
    .FirstOrDefault(d => d.Status == ContentLifecycleStatus.Live && d.Visible);

TrackedList<Guid> tags = pressRelease.GetValue<TrackedList<Guid>>("Tags");

Note that the collection returned is not a standard List, but rather a TrackedList. TrackedList is a construct from OpenAccess, the ORM Sitefinity utilizes and what sits between your code and the database. It mostly functions like you would expect a regular List, but has additional methods and properties that are beyond the scope of this post. It is only important to note here so that when you call GetValue, you feed it the correct type. You need to add “using Telerik.OpenAccess;” to the top of your file in order to have the class available. Be aware that even if you restrict the field on the custom content type to only allow a single classification to be selected, you still work with a TrackedList. Much like how in the previous post you still work with the nullable decimal type even when the field can’t be null, Sitefinity stores the classification relationship the same way regardless.

So now we have the IDs of the Tags. If we tried to display them, we’d of course just see GUIDs instead of tag names. As a consequence, there’s an additional step required in order to display the Tags’ Title properties (i.e. the user-friendly names):

TrackedList<Guid> tagIds = pressRelease.GetValue<TrackedList<Guid>>("Tags");
var taxonomyManager = TaxonomyManager.GetManager();
var tagTaxonomy = taxonomyManager.GetTaxonomy(TaxonomyManager.TagsTaxonomyId);
IEnumerable<string> tagTitles = tagTaxonomy.Taxa.Where(t => tagIds.Contains(t.Id)).Select(t => t.Title.ToString());

Here, we’re using Sitefinity’s TaxonomyManager, and fetching the Taxonomy for Tags by passing in the TagsTaxonomyId (which is available as a static property of TaxonomyManager). Once we have that, we access that Taxonomy’s Taxa (i.e. the Tags currently in Sitefinity), and tell it to return only the ones that match the IDs assigned to our Press Release. Once we have the tag titles (we call ToString() because Title is an LString), we can assign them to a “Tags” property in a domain object, string.Join() them together as a comma separated list, or do whatever else we like.

NOTE: The taxa have two properties that both look like the display text you wish to use: “Title” and “Name.” “Name” refers to the “name used in code” that developers can set on classifications in the Sitefinity backend. Title is the actual label that we want to display to users throughout the site (and is what Sitefinity uses in its built-in Tag widgets). Whenever working with classifications, use “Title” as the label/friendly version to display instead of “Name.”

For fetching Categories attached to a given custom content type, it’s very much the same procedure. The only two differences are that instead of passing “TaxonomyManager.TagsTaxonomyId” to the GetTaxonomy method, you pass “TaxonomyManager.CategoriesTaxonomyId” instead; and the name of the field on the custom content type will be “Category” instead of “Tags.”

For custom classifications, it’s the same: Acquire the ID of the custom classification taxonomy, change the field name fetched to that of your custom classification on the custom content type, and you’re done!

Querying

Whenever you’d like to query a custom content type by a classification, you have to do so by its ID. If you don’t already have the ID handy (e.g. a user has a box where they can input the Tag or Category by name), you have to go through the TaxonomyManager to fetch the classification’s ID. Once you have the ID, you simply call “Contains” on the retrieved TrackedList collection within your Where() LINQ statement to perform the filter:

Guid tagId = /* GUID either passed in, or fetched from TaxonomyManager. */;
DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType)
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible)
    .Where(d => d.GetValue<TrackedList<Guid>>("Tags").Contains(tagId));

With this query, you’ll get all Live/Published Press Releases that are tagged with the given Tag. You can use basic or/and logic if you want more complex filtering (e.g. “return any Press Releases that contain any tags” or “return any Press Releases that have all of these tags”) in the LINQ itself.

Just like when fetching classifications, the only difference when trying to query by a different classification (either by Category or a custom one) is the field you query on.

Related Media (Document, Image, Video)

Custom content types can also have fields that store relations to other media content in your Sitefinity site. In this instance, a Related Media field allows users to assign Images, Documents/Files, and Videos to a given piece of custom content. Typically, you don’t query or filter content by what images/documents/videos are associated with it, so in this section we’ll just focus on fetching the related media from custom content types.

When attempting to fetch related media, Sitefinity returns it as a List of IDataItem. If you were to use the GetValue() method like this, that is what you’d get in return. However, there’s a much easier way to work with related media, so you don’t have to do any manual fetching of metadata (like what occurs with classifications): Instead of calling GetValue<List<IDataItem>>(), you can call GetRelatedItems to fetch a strongly-typed object. For Related Media, the three types you use are:

  • Telerik.Sitefinity.Libraries.Model.Image
  • Telerik.Sitefinity.Libraries.Model.Document
  • Telerik.Sitefinity.Libraries.Model.Video

It’s important to verify that you are using the right “Image” (or Document or Video) classes in your code. Many other libraries have an Image class (or the others), so be sure that you are utilizing the one provided in Sitefinity’s Model namespace. putting the namespace up as a using right away is the easiest way to make sure your IDE doesn’t try to include other namespaces. With that in mind, here’s an example of fetching these different kinds of related media from Sitefinity:

IQueryable<Image> images = pressRelease.GetRelatedItems<Image>("MyImages");
IQueryable<Document> documents = pressRelease.GetRelatedItems<Document>("MyDocs");
IQueryable<Video> videos = pressRelease.GetRelatedItems<Video>("MyVids");

With these strongly-typed objects at your disposal, you instantly have access to all the important information for your related media: Titles, Alternative Text (for images), URLs to the media itself (via the “MediaUrl” property), etc.

There are many shared properties across these media types. You can call a more generic method, GetRelatedItems<MediaContent>(), and still get your related media items (MediaContent is in the same namespace as Image/Document/Video). While you won’t have access to specific properties (e.g. Alternative Text for Images), you can use this to your advantage if you’re writing some generic code for media, or if you don’t care about the specific properties and just want titles and media URLs. Here’s a sample where we just care about the latter, and want to extend Dynamic Content so that we can easily access its related media’s URLs:

/* Can define this in an Extensions class */
public static IQueryable<string> GetMediaUrls<TMedia>(this DynamicContent dataItem, string fieldName)
    where TMedia : MediaContent
{
    return dataItem.GetRelatedItems<TMedia>(fieldName).Select(m => m.MediaUrl);
}

/* Usage Example */
IQueryable<string> iamgeUrls = pressRelease.GetMediaUrls<Image>("MyImages");
IQueryable<string> docUrls = pressRelease.GetMediaUrls<Document>("MyDocs");
IQueryable<string> vidUrls = pressRelease.GetMediaUrls<Video>("MyVids");

In this simplified example, we are able to leverage the generic “MediaContent” type for Sitefinity Related Media, and provide a quick, useful little extension method for dynamic content, which saves us from having to dip into the objects to get the right property we want, and write cleaner code. You could take this example further, and create a generic “Media” domain object in your business logic, with simple properties like Title and URL, and have the extension method return that instead. Sky’s the limit!

GetRelatedItems Pitfall

One issue I come across is that GetRelatedItems() will, under certain conditions, not return any results. This is because in order for GetRelatedItems() to work, the collection of data items you’re working with must be within an iterator (i.e. a foreach loop) or in memory (i.e. calling ToList() after the Where()). If neither of these conditions are followed, then Sitefinity does not load the related data in its objects, which results in no related items getting returned. The following code example, which returns a collection of image collections (one per Press Release), will not return any Images, even if they have some!

var imageCollections = dynamicModuleManager.GetDataItems(type)
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible)
    .Select(d => d.GetRelatedItems<Image>("MyImages")); // Returns nothing!

You can avoid this by either adding “.ToList()” after the Where() but before the Select() (so that Sitefinity loads the related data before you attempt to access it), or iterate over the “dataItems” variable in a foreach loop, and calling GetRelatedItems in there (dropping the Select() altogether). When you do either of these things, make sure that your filtering is not taking place in-memory, but rather before you call ToList() or start iterating. If you’re not careful, you could bring in your entire data set from the database, only to filter 99% of it out! Below are two examples where related items are successfully pulled down:

Using ToList()
var imageCollections = dynamicModuleManager.GetDataItems(type)
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible)
    .ToList() // Ensures related items load; do not put this before the Where()!
    .Select(d => d.GetRelatedItems<Image>("MyImages"));
Using foreach
var pressReleases = dynamicModuleManager.GetDataItems(type)
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible);

foreach (var pressRelease in pressReleases)
{
    // Related items load within the foreach iterator.
    var images = pressRelease.GetRelatedItems<Image>("MyImages");
}

Next Time: Related Data

One of the most powerful (and complex!) kinds of data you can have on your custom content types is other custom content types. These come in the form of “Related Data” and can be used to associate one kind of content with another, in whatever way makes sense in your own business domain logic. You can even access content types in other custom modules! As an example use case, a Press Release might have a list of Related Companies that are associated with a Release. And “Company” can be its own custom content type with its own properties. In my next post, I’ll tell you all about Related Data, and will also touch on working with custom content types set up in a parent/child relationship (a different method of “relating” data together).

The post Complex Custom Properties for Sitefinity Content appeared first on Falafel Software Blog.

Relating Data in Sitefinity Content

$
0
0

In my previous post discussing properties on custom content, I delved into some of the more complex types that can be properties, including classifications and related media. This post will get into the most complex type to work with: Related Data. This is taking existing custom content types and allowing other content types to have a property that relates one to the other. Relating data can happen with built-in content types (e.g. News), extend across other module builder modules, and even be self-referential! On top of all that, you can even create a custom content type that is the parent or child of another custom content type (within the same module). It is a very powerful feature that Sitefinity offers, and can make the translation from business logic to real Sitefinity code and content an easy step. Let’s get to it.

Querying Related Data

In actuality, if you’ve been following my posts, you already know how to fetch related data! If you recall, back in my other post we went over how to fetch Related Media (Images, Documents/Files, and Videos). The syntax and methods used there are pretty much what we use here. The only difference is the type we pass to GetRelatedItems(): DynamicContent.

In the following example, I created a new custom content type in the PressReleases module, called “Company.” The Company type has two properties: Title and PressReleases, the latter of which is of type Related Data, and the data type is PressRelease. GetRelatedItems() returns the Live version of the related data (the same is true when fetching Related Media). And since these objects are just more DynamicContent objects, you can utilize the same GetValue() methods to fetch specific properties of the related data.

Type type = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.Company");
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager();
IQueryable<DynamicContent> companies = dynamicModuleManager.GetDataItems(type)
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible);
foreach (DynamicContent company in companies)
{
    // Fetch related data just like related media, only passing DynamicContent to GetRelatedItems.
    IQueryable<DynamicContent> pressReleases = company.GetRelatedItems<DynamicContent>("PressReleases");

    // We can now iterate over the company's collection of Press Releases
    // and use them like any other DynamicContent object.
    foreach (DynamicContent pressRelease in pressReleases)
    {
        string pressReleaseTitle = pressRelease.GetString("Title");
    }
}

Filtering by Related Data

Let’s say you wanted to find all the Companies that have a specified Press Release related to them. One might first try starting with getting companies from GetDataItems(companyType), then using Where() to select and filter by the Press Releases returned from GetRelatedItems. That doesn’t work, unfortunately, due to the nature of how GetRelatedItems works, and due to how the LINQ translates back into SQL (or whatever your backing data store might be). Worry not, however, for there is a solution! You just have to work from the other direction. Rather than starting with a collection of Company objects and filtering them, instead start with the Press Release you want to filter by, and fetch all Companies that are related to it.

DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager();

// Can get an ID by first querying Press Releases based on some search criteria (e.g. a matching Title).
Guid livePressReleaseId = new Guid("c60d2d01-53ee-6d8d-a049-ff0000513041");
Type pressReleaseType = TypeResolutionService
    .ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.PressRelease");
DynamicContent pressRelease = dynamicModuleManager.GetDataItem(pressReleaseType, livePressReleaseId);

// From the Press Release, get any Companies that have it selected as a Related Data item.
Type companyType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.Company");
const string relatedDataFieldName = "PressReleases"; // The property name storing Related Data on the Company type.
const string providerName = null; // Unless you have custom providers defined, null can be passed in.
IQueryable<DynamicContent> companies = pressRelease
    .GetRelatedParentItems(companyType.FullName, providerName, relatedDataFieldName)
    .OfType<DynamicContent>()
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible);

First we acquire the Press Release DynamicContent object that we want to filter by. Above I just did a query ahead of time and hardcoded a GUID representing a Live, Visible Press Release in my database. In your code you can get it dynamically using the same fetching/filtering techniques I’ve demonstrated in previous posts. From there, we call the key method on the DynamicContent object: GetRelatedParentItems. In this case, the method is taking in the type of the parent (Company), the provider name (you can always pass null here if you haven’t made any custom providers), and the related data field name on Company (“PressReleases”). We then strongly type it using OfType, and proceed to use the same Where() clause we’ve been using to only get Live, Visible Companies.

We have to use OfType<>() because the generic GetRelatedParentItems<>() method does not work for this scenario! But with we’ve written above, we can fetch any collection of Companies filtered by a particular Press Release. It is not the most intuitive, but it is at least possible to do using the built-in Sitefinity methods. And if you bookmark this blog post, you will never forget. 🙂

All Kinds of Data to Relate

In the above examples we’ve been using the simplest Related Data case: Two custom content types within the same module builder module. Related Data, however, can expand beyond that simple scenario. When setting up your custom content type, Sitefinity provides a drop down list of all the possible options for data to related to. In addition to other types within the same module, you can select:

  • Other custom content types in other installed/activated modules within the Module Builder.
  • Built-in content types within Sitefinity, such as News and Blog Posts
  • The custom content type you are currently editing. Note, however, that this won’t be an option when initially creating the type, because the type has to exist before it appears in this list.

When working with related data concerning other custom content types, the code is exactly the same as above. When working with built-in types, though, you don’t use DynamicContent as the object to work with. Sitefinity has defined model classes (think domain objects) for their built-in types that you use instead. These have advantages over DynamicContent since Sitefinity can specify strongly-typed properties specific to a given type. This can’t be done, naturally, with dynamically-created DynamicContent types.

I have added a “MyNews” Related Data property to the Company content type. Below is a simple example of accessing this property, and each related news item’s properties:

Type companyType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.Company");

IQueryable<DynamicContent> companies = dynamicModuleManager.GetDataItems(companyType)
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible);

foreach (DynamicContent company in companies)
{
    // Telerik.Sitefinity.News.Model.NewsItem (same namespace as Image, etc).
    IQueryable<NewsItem> news = company.GetRelatedItems<NewsItem>("MyNews");
}

Parent/Child Custom Content Types

The final subject we’re touching on is that of parent and children custom content types. When relating data between two types, it often makes more sense to make one the parent of another, instead of simply having a related data property on the type. In this case, I created a “Division” custom content type, that is a child of Company. Unlike Related Data, you can only make a new content type a child of a content type that exists within the same module. You can’t make child content types for built-in types, cross-module types, and (of course) itself. In this case, Division is a child of Company, and Division has just one field for simplicity: Title.

Pitfalls to Avoid

Despite the name of the method we used in the previous section, we will not be using “GetRelatedParentItems” here. That method only functions with Related Data, not when relating data in a parent/child scenario. In addition, there are other “gotcha” properties on DynamicContent that look to be entirely relevant, but are in fact never set by Sitefinity. “SystemHasChildItems” is a boolean, and “SystemChildItems” is a collection of objects. They will never be set and are always false and null, respectively. According to documentation, these properties exist to “improve performance”, for you (the developer) to assign manually so that you don’t call the real data-access methods multiple times. When trying to initially hydrate your data or check to see if your item has children, avoid these!

The Real Methods, With One More Caveat

In actuality, you have to use the DynamicModuleManager to gain access to a parent’s children. While the above pitfalls can snag you and make you fall into a trap, the fact that Sitefinity does not load children by default makes sense: If you’re working with 1,000 Companies and you only care about Divisions when drilling down into a Company’s details, there’s no reason to go and fetch all 1,000 Companies’ Divisions! Instead, Sitefinity lets you make the call there. The DynamicModuleManager has two methods that are very similar to the named properties above: HasChildItems() and GetChildItems(). Both take in a DynamicContent and will return whether or not it has children (the former) and the children itself (the latter).

One last thing to watch out for: Unlike all of the previous data-fetching methods we’ve worked with, this one (GetChildItems()) does not give you the Live version of the data. It returns the Master version of the data instead. In earlier versions of Sitefinity, GetRelatedItems() did the same thing, but Sitefinity changed the behavior. For GetChildItems(), however, we still have to make an additional step in order to retrieve the Live versions of our Divisions.

Time for the Code

Below is a fully-functional example of iterating over a Company’s Divisions, first checking if any exist, and then accessing a Division’s Title property.

DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager();
Type companyType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.Company");

IQueryable<DynamicContent> companies = dynamicModuleManager.GetDataItems(companyType)
    .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible);

Type divisionType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.Division");
foreach (DynamicContent company in companies)
{
    bool hasDivisions = dynamicModuleManager.HasChildItems(company);
    if (!hasDivisions)
    {
        continue;
    }
    IQueryable<Guid> masterDivisionIds = dynamicModuleManager.GetChildItems(company).Select(c => c.Id);
    IQueryable<DynamicContent> liveDivisions = dynamicModuleManager.GetDataItems(divisionType)
        .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible)
        .Where(d => masterDivisionIds.Contains(d.OriginalContentId));
    foreach (DynamicContent division in liveDivisions)
    {
        // division.SystemParentItem contains the master version of the parent Company DynamicContent object.
        string divisionTitle = division.GetString("Title");
    }
}

Here we are applying the described steps in order to fetch a Company’s Divisions. For each Company, we check to see if it has Children. If so, we get them via the provided GetChildItems() method on dynamicModuleManager, selecting their IDs. With those IDs, we then query Sitefinity via the dynamicModuleManager again, this time passing in the Division type, and filtering down to Live, Visible items whose OriginalContentId is in our collection of Master IDs. From there we have access to the Live Divisions of the given Company.

Sitefinity bridges the parent/child gap the other direction as well. As you can see in the inner foreach comment, the Division itself exposes the parent, via the SystemParentItem property (and unlike the other two “System” properties that are never set by Sitefinity, this one actually is set by Sitefinity). But just like with GetChildItems(), it exposes details about the Master version, not the Live version. If you have a child and want its Live parent, you have to do the same extra step of getting the Live version by OriginalContentId.

End of the Road

These posts going over the properties for custom content types are certainly not an exhaustive coverage of every possible type of property you can work with, but it does hit on all the most-used ones, simple and complex. I covered Related Data properties and Parent/Children types in this post, Classifications and Related Media in my previous post, and simple types (strings and numbers) in an older post. We’ve covered a lot of ground here! With these teachings at your disposal, you will walk away a stronger Sitefinity developer when it comes to developing with the Module Builder.

The post Relating Data in Sitefinity Content appeared first on Falafel Software Blog.

Working with Hybrid Forms in Sitefinity Widgets

$
0
0

Throughout the course of web development, you often find yourself in a situation where you need data input from your user. Scenarios range from a simple contact form, to something as complex as a multi-part job application. In either case, Sitefinity offers multiple solutions. The easiest solution, for the simpler forms such as the aforementioned contact form, is to use Sitefinity’s built-in Forms capabilities. Much like with their pages, Sitefinity Forms provide a very simple, powerful way to create and customize even more complex forms for your site. The resulting data is then stored in Sitefinity, and can be set to notify anyone of new submissions and have its data exported in an Excel format.

Despite these built-in features, you’ll still find yourself needing to create your own forms from scratch from time to time. Sitefinity MVC widgets are another powerful means to extend your Sitefinity application, and within them you can create forms as well. Unlike the forms created in the backend, however, these are completely customizable, as you have 100% control over your widget’s Razor views. These are hybrid forms, forms which leverage ASP.NET’s already-existing <form> tag and submit data to your MVC controller. If you are working on anything requiring more than what the out-of-the-box Sitefinity Forms can easily provide, this is for you!

Initial Setup

To start, we’ll define a basic Sitefinity MVC widget controller, with two actions: The Default action which is invoked when first hitting the page, and the SubmitForm action for when the form is submitted by the user. In addition, we’ll have a blank viewmodel class called “MyFormViewModel” which we’ll populate with the properties we want to capture as we progress.

[ControllerToolboxItem(Name = "CustomFormWidget", Title = "Custom Form Widget", SectionName = "MvcWidgets")]
public class CustomFormWidgetController : Controller
{
    public ActionResult Index()
    {
        return View("Default", new MyFormViewModel());
    }

    [HttpPost]
    public ActionResult SubmitForm(MyFormViewModel viewModel)
    {
        throw new NotImplementedException();
    }
}

public class MyFormViewModel { } // To be populated!

Out of the box, Sitefinity sites have an MVC folder, with subfolders for your Controllers, Models (viewmodels), and Views. Our Controller is CustomFormWidgetController, so for its view, we’ll create a subfolder called “CustomFormWidget” inside the Views folder, and a blank “Default.cshtml” within the subfolder.

01-mvc-folder-structure

Inside Default.cshtml, here is the bare minimum needed to instantiate a form that our controller can tap into and utilize.

@using Telerik.Sitefinity.UI.MVC
@model SitefinityWebApp.Mvc.Controllers.MyFormViewModel

@using (Html.BeginFormSitefinity("SubmitForm", "MyFormName"))
{
    @* The form's elements will go here! *@
}

This snippet of code instantiates a form with a name (“MyFormName”), and specifies the MVC Action to call when it’s submitted (“SubmitForm” — the other action we defined earlier).

BeginFormSitefinity and the ID of the Page Form Tag

“BeginFormSitefinity” is an extension method on Html that is built into Sitefinity (hence the inclusion of a using statement at the top). Note that it differs in implementation from the MVC BeginForm method, in that it doesn’t insert a <form> tag on the page. Instead, it inserts JavaScript that submits the form, including any elements inside the using curly braces and their inputted values. It is implemented this way so that the form can be created even in hybrid Sitefinity pages (the default kind of Pages you create in the Sitefinity backend). These pages already have a <form> tag wrapping the majority of the page (standard ASP.NET fare), and only one <form> tag can exist in this context.

While this works well enough, it does lend itself to an issue that I and many others have run into multiple times. If you have a master page or custom Sitefinity template, and you give the form an ID other than the default “aspnetForm”, the form won’t submit! You’ll get a JavaScript error instead (which you’ll only see if you have your browser’s dev console open).

This is because the JavaScript Sitefinity writes to the page depends on the ID of the form being exactly “aspnetForm” and nothing else. If you have control of the <form> tag on your template or page, and are not depending on the ID being different, go ahead and change it to “aspnetForm.” If you cannot change the form ID, however, then you can insert the following simple snippet of jQuery to update the <form> tag’s ID client-side, so that BeginFormSitefinity will work:

<script type="text/javascript">
    $("form").attr("id", "aspnetForm");
</script>

This little hack should get your Sitefinity MVC widget-driven form running if you’re finding it not submitting.

The Submit Button

The simplest form you can make here is simply one that allows you to click a button and execute the given action. This might sound pointless, but in actuality this is a very simple way to create a client-facing button that activates some action, program, task, etc. in your Sitefinity application. Since Sitefinity MVC widgets can be placed on pages in the backend as well as the frontend, it’s a nice quick way to get a simple “you can perform this action whenever you feel like” functionality up and running.

The submit button itself is nothing special. It is literally an HTML submit input (I wrapped it in a div for neatness when displayed in a browser):

@using (Html.BeginFormSitefinity("SubmitForm", "MyFormName"))
{
    <div>
        <input type="submit" value="Submit Form" />
    </div>
}

Currently, our controller will do nothing but throw a NotImplementedExcpetion(). This does tell us something, though: That our form is successfully wired up to our controller, and is calling the right action. We’ve overcome the potential form ID issue, and are ready to begin adding data to our viewmodel and our form itself.

Basic Data: Strings and Numbers

I won’t get into too much detail in this section, as creating textboxes, radio buttons, checkboxes, etc. is pretty much identical to how you would create form fields in non-Sitefinity MVC forms. You can use HTML helper methods defined in MVC to generate the labels and inputs. We’ll also update our viewmodel with a couple fields. Here’s what our form and viewmodel class look like now (hybrid forms, at the end of the day, look quite similar to MVC forms):

@using (Html.BeginFormSitefinity("SubmitForm", "MyFormName"))
{
    <div>
        @Html.LabelFor(m => m.TextField)
        @Html.TextBoxFor(m => m.TextField)
        @Html.ValidationMessageFor(m => m.TextField, "Please enter some text", new {@class = "error"})
    </div>
    <div>
        @Html.LabelFor(m => m.NumberField)
        @Html.TextBoxFor(m => m.NumberField, new { min = "0", type = "number" })
        @Html.ValidationMessageFor(m => m.NumberField, "Please enter a number", new {@class = "error"})
    </div>
    <div>
        <input type="submit" value="Submit Form" />
    </div>
}
public class MyFormViewModel
{
    public int NumberField { get; set; }
    public string TextField { get; set; }
}

Form Validation: Client and Server-Side

In addition to defining a textbox and label for both fields, we also define a validation message section. This text will only appear when the Model State is invalid. This validation can occur automatically on the client side, but it is also vital to validate the inputs server-side as well. A user can always bypass any and all client-side validations, feeding your method any data they want. It is up to you to validate server-side as well. That being said, client-side validation is convenient to the user as form submission can be blocked until the correct data is inputted.

In the TextBoxFor the number field, you see we define its type as number, with a minimum value. This provides convenience to the user (only lets them type numbers, doesn’t submit the form if the number is too low, etc) and makes sure basic validation takes place before form submission. On submit, you can check on the numeric value (the model binder can only bind numbers to your viewmodel, since “NumberField” is an integer), and do the usual MVC “ModelState.AddModelError” and “ModelState.IsValid” testing, before proceeding with handling the form submission. You can enforce requirements not defined on the client, as well. Taking these into account, here’s what our SubmitForm action could look like:

[HttpPost]
public ActionResult SubmitForm(MyFormViewModel viewModel)
{
    if (string.IsNullOrWhiteSpace(viewModel.TextField))
    {
        ModelState.AddModelError("TextField", "Please enter some text.");
    }

    if (viewModel.NumberField < 0)
    {
        ModelState.AddModelError("NumberField", "Please enter a valid number.");
    }

    if (!ModelState.IsValid)
    {
        // Return the submitted viewModel to the view, so that form fields are pre-populated on reload.
        return View("Default", viewModel);
    }
    
    // Handle the form here, since the fields are validated.
    throw new NotImplementedException();
}

Submitting Files

With checkboxes, radio buttons, drop down lists, textboxes, and textareas, the data submitted can be easily captured as strings and numbers. Files, however, are another matter altogether. It takes a little bit of tweaking to the page content, but other than that file submission in hybrid forms is a cinch.

Setting Enctype on the Form

In order for files to be successfully submitted, you have to set “enctype” to “multipart/form-data” on your page’s <form> tag. You don’t want this on every page, of course, so if your <form> is on every page (part of a template/ASP.NET master page, etc.), you can add it just to your form by adding the following snippet to your view:

<script type="text/javascript">
    (function ($) {
        "use strict";
        $("form").attr("enctype", "multipart/form-data");
    })(jQuery);
</script>

If you don’t, then when you submit the form, the variable the file gets saved to will be null.

The Field and the ViewModel Property

The field itself is a normal HTML field, whose type is set to file. In your viewmodel, create a property of type “HttpPostedFileBase.” This property will house the selected file, and have metadata on it as well (MIME type, size, etc). Adding those things makes our current view and viewmodel look like the following:

@using (Html.BeginFormSitefinity("SubmitForm", "MyFormName"))
{
    <div>
        @Html.LabelFor(m => m.TextField)
        @Html.TextBoxFor(m => m.TextField)
        @Html.ValidationMessageFor(m => m.TextField, "Please enter some text", new {@class = "error"})
    </div>
    <div>
        @Html.LabelFor(m => m.NumberField)
        @Html.TextBoxFor(m => m.NumberField, new { min = "0", type = "number" })
        @Html.ValidationMessageFor(m => m.NumberField, "Please enter a number", new {@class = "error"})
    </div>
    <div>
        @Html.LabelFor(m => m.UploadedFile, "My File")
        @Html.TextBoxFor(m => m.UploadedFile, new { type = "file" })
        @Html.ValidationMessageFor(m => m.UploadedFile, "Please upload a file", new { @class = "error"})
    </div>
    <div>
        <input type="submit" value="Submit Form" />
    </div>
}

<script type="text/javascript">
    (function ($) {
        "use strict";
        $("form").attr("enctype", "multipart/form-data");
    })(jQuery);
</script>
public class MyFormViewModel
{
    public int NumberField { get; set; }
    public string TextField { get; set; }
    public HttpPostedFileBase UploadedFile { get; set; }
}

Now, in our SubmitForm action, we’ll receive the file as well as the other form data, to do with what we please.

Wrapping Up

As demonstrated above, MVC and Sitefinity provide you with a lot of power when it comes to creating customized hybrid forms that Sitefinity’s out-of-the-box form creation tools can’t handle on their own. You can create something as simple as a single button “push to activate feature” form, or very complex forms that accept multiple types of data and files. You can supply custom validation and create detailed error messages, all while leveraging out-of-the-box MVC functionality to leave the amount of code you have to actually write at a minimum.

The post Working with Hybrid Forms in Sitefinity Widgets appeared first on Falafel Software Blog.


Customizing Search Result URLs in Sitefinity

$
0
0

One of Sitefinity’s many features is its search capabilities. Using Lucene under the hood, Sitefinity indexes your site content, both built-in content types and dynamic content types, along with static HTML, and allows you to present organized results to the user. You can customized what is and isn’t indexed among other things. Most of the time, Sitefinity is smart enough to create search result URLs that take you directly to the content in question. If you have “/my-news” as your news page, for instance, a particular article URL might look something like “/my-news/2015/1/1/my-article-url”. The search indexer automatically takes the page where content is hosted into account, and creates the proper URL in its search results.

There are occasions where Sitefinity cannot figure out exactly where to take a user, however. Sometimes you may have a customized URL that doesn’t fit the default Sitefinity content URL scheme. And even though you can customize the URL scheme of content, if you have lots of existing content already, you’d have to go and re-publish every single one in order to re-apply the new scheme to them! Whatever the scenarios may be that require customization of search result URLs, there is a way to override the default behavior for URL construction, and supply your own URLs for built-in content and dynamic content. Doing so allows you to fully customize search result URLs without having to dig too deep in the search index implementation or having to write your own custom search indexing solution.

Creating a Custom Pipe

In order to customize search result URLs, you need to create a custom Pipe (based off existing ones so that 99% of the work is done for you) and override its “GetItemUrl” method. If you want to change the URL of a built-in content type, such as News, you’ll want to create a class derived from Telerik.Sitefinity.Publishing.Pipes.ContentInboundPipe. For dynamic content types, the class to derive from is Telerik.Sitefinity.DynamicModules.PublishingSystem.DynamicContentInboundPipe. I have done so in the code samples below.

public class CustomContentInboundPipe : Telerik.Sitefinity.Publishing.Pipes.ContentInboundPipe
{
    protected override string GetItemUrl(IContent contentItem)
    {
        return base.GetItemUrl(contentItem);
    }
}

public class CustomDynamicContentInboundPipe 
    : Telerik.Sitefinity.DynamicModules.PublishingSystem.DynamicContentInboundPipe
{
    protected override string GetItemUrl(DynamicContent contentItem)
    {   
        return base.GetItemUrl(contentItem);
    }
}

In both cases, you see that a string is expected out of the GetItemUrl method. This is our entry point for customizing Sitefinity search result URLs. We are given the content item to reference if we need a specific property, and can use that to craft whatever URLs we please. The URLs can be relative, so you don’t have to worry about domain names: You can return “/foo/bar/my-article” and it will display correctly on your web site. In fact, let’s use that as our example for customization! For any news item, instead of the default, let’s make the URL on the search results page for news articles link to “/foo/bar/{news-item-url}.”

public class CustomContentInboundPipe : Telerik.Sitefinity.Publishing.Pipes.ContentInboundPipe
{
    protected override string GetItemUrl(IContent contentItem)
    {
        if (contentItem.GetType() == typeof(NewsItem))
        {
            return "/foo/bar" + contentItem.GetPropertyValue<Lstring>("UrlName");
        }
        return base.GetItemUrl(contentItem);
    }
}

Our method does a couple of things: First, it verifies that the content we’re dealing with currently is, in fact, a NewsItem. If it is not, we call the base method so that the default behavior is preserved. Note that many built-in content types will come through this method, which is why we have to make sure we’re working with a NewsItem before proceeding.

Once we’ve confirmed the type, we go ahead and return our desired custom URL. We access the item’s “UrlName” property and append that to our template of “/foo/bar/” and that’s it!

The dynamic content method is implemented similarly: Verify you’re working with the right type, then return a custom URL based on your template. Otherwise, preserve the default behavior.

public class CustomDynamicContentInboundPipe
    : Telerik.Sitefinity.DynamicModules.PublishingSystem.DynamicContentInboundPipe
{
    protected override string GetItemUrl(DynamicContent contentItem)
    {
        Type customContentType = Telerik.Sitefinity.Utilities.TypeConverters.TypeResolutionService
            .ResolveType("Telerik.Sitefinity.DynamicTypes.Model.CustomModuleName.CustomContentTypeName");
        if (contentItem.GetType() == customContentType)
        {
            return "/dynamic/custom/url/" + contentItem.UrlName;
        }

        return base.GetItemUrl(contentItem);
    }
}

The only differences here are that UrlName is a strongly-typed property on DynamicContent (no need for GetPropertyValue), and we have to resolve our dynamic type using the TypeResolutionService, since dynamic types do not have strongly-typed classes associated with them.

Registering the Custom Pipes

Now that we have our customized classes, we have to register them with Sitefinity. Otherwise, the search indexer won’t know to call them when it is reindexing your site! To do so, we’ll be adding a couple lines of code to the “Bootstrapped” event that fires on startup. We’ll hook into this event in the Global class (Global.asax.cs). In addition, we have to unregister the default pipe before registering our custom one. All of this is demonstrated below:

public class Global : HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        Bootstrapper.Initialized += OnBootstrapperInitialized;
    }

    private static void OnBootstrapperInitialized(object sender, ExecutedEventArgs e)
    {
        if (e.CommandName == "Bootstrapped")
        {
            PublishingSystemFactory.UnregisterPipe(ContentInboundPipe.PipeName);
            PublishingSystemFactory.RegisterPipe(ContentInboundPipe.PipeName, typeof(CustomContentInboundPipe));

            string customContentTypePipeName 
                = "Telerik.Sitefinity.DynamicTypes.Model.CustomModuleName.CustomContentTypeNamePipe";
            PublishingSystemFactory.UnregisterPipe(customContentTypePipeName);
            PublishingSystemFactory.RegisterPipe(customContentTypePipeName, typeof(CustomDynamicContentInboundPipe));
        }
    }
}

Here we unregister the pipes for ContentInboundPipe (built-in content) and for our particular dynamic content type (which is simply the full content type name with the word “Pipe” at the end). Then we register new pipes, using the same names, but point to our custom classes instead. Because we inherited from the normal classes, these registrations are valid.

Applying the Changes to the Search Results

Once these are in place, restart your Sitefinity application and go to the backend, navigating to the Search Indexes management page (under Administration). Re-index your Sitefinity search index to have Sitefinity invoke your custom classes and overridden methods. If you debug and put breakpoints in your methods, you’ll see them hit once for every piece of content under the given type. After the re-index is complete, perform a search, and you’ll find that your custom search result URLs are in place in your search results!

The post Customizing Search Result URLs in Sitefinity appeared first on Falafel Software Blog.

Sitefinity Status Page HTTP Response Codes

$
0
0

When working with the new Web Services module or WCF services in Sitefinity, you want to be able to allow the requesting clients to properly respond to and handle a failed request.
This is generally simple enough, as a failed request would usually be accompanied by an HTTP status code that indicates something went wrong. The status can be reviewed by the client and handled appropriately based on the status code (such as 404 for not found or 500 for a server error).

To demonstrate this let’s look at this sample code, which uses the Web Services module API to retrieve news items and append them to a list:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
    <meta charset="utf-8" />
</head>
<body>
    <ul id="news"></ul>

    <button id="loadNews">Go</button>

    <script type="text/javascript">
        $(function () {
            $("#loadNews").on("click", function () {

                $.get("/api/default/newsitems", function (result) {
                    $("#news").empty();

                    $.each(result.value, function (index, value) {
                        $("#news").append("<li>" + value.Title + "</li>");
                    });
                }).success(function (result) {
                    console.log("Success!");
                }).error(function (result) {
                    if (result.responseJSON)
                        console.log("Something went wrong: " + result.responseJSON.error.innererror.message);
                    else
                        console.log("Something went wrong: " + result.statusText);
                });
            });
        })
    </script>
</body>
</html>

If everything goes well, we get a success code, and can process each item, adding it to the list:

News-Items-List

If there is an error, such as protecting the content from anonymous access, we’ll get an error like 401, which we can handle appropriately. In this case we’ll just show the expected error in the console.

api-401-error

However, this all goes out the window when your Sitefinity site experiences a restart, during which all requests are 302 redirected to this familiar screen:

sitefinity-status-page-200

Unfortunately, in addition to redirecting content pages and public resources, this screen is also returned for API calls, and does so with a 200 (Ok) status code.

As a result, even though technically this resulting page should be treated as an error by our code, it interprets it as a success. You can see the resulting HTML in this screenshot, where the success result of the API call is invoked:

sitefinity-api-call-status-200

Why does this happen?

According to the official documentation for the application status page, the 302 redirect to the Sitefinity status page is done to ensure that search engines to not attempt to index your site during initialization, which could possibly affect your SEO score. Instead, the temporary redirect instructs search engines to try again later.

While this is great for your search engine ranking, it may affect the performance of your custom widgets that use AJAX to get their data from your site.

How do I fix it?

Fortunately, in Sitefinity 9.1+ it is possible to change the status code returned with the status page to something other than success by adding a key named sf:AppStatusPageResponseCode with the appropriate code as the value.

In this case, I think perhaps the 503 (Service Unavailable) code makes the most sense:

<add key="sf:AppStatusPageResponseCode" value="503" />

Now, even though the status page is still returned (as evidenced by the HTML returned in the error handler), it is now returned with our selected error code:

sitefinity-status-page-503

meaning that we can now handle the temporary outage gracefully and show an appropriate response to the user.

sitefinity-api-call-503

As always, I hope this is helpful, and thanks for reading!

P.S. If you want to learn more about Sitefinity Web Services, be sure to check out the newly updated Sitefinity 9 for Developers Book which contains a brand new chapter discussing both the new Web Services module in SF9 as well as a refresh of the content on WCF services.

The post Sitefinity Status Page HTTP Response Codes appeared first on Falafel Software Blog.

Accessing Sitefinity Roles in Code

$
0
0

When developing custom solutions in your Sitefinity application, you may need the ability to work with Sitefinity roles. As a developer, you would reach out to the RoleManager that Sitefinity provides, and access the roles from there. However, you may run into an issue where some roles, verified to be a part of your site, are not represented when working with the RoleManager. This post goes into this issue, and shows how to access all Sitefinity roles to work with.

Basic Role Access

Let’s say you want to list all available Sitefinity roles on your web site. The natural idea would be to access the RoleManager, then call GetRoles() (or GetRoleNames() if you only care about the names), iterate the returned collection and be done. On a fresh Sitefinity install (9.1), I created a couple custom roles “CustomRole1” and “CustomRole2”, then proceeded to call the RoleManager to grab my roles and display them:

RoleManager.GetManager().GetRoleNames()

Done, right? Wrong! The roles we end up seeing are the following:

  • Customers (ecommerce)
  • CustomRole1
  • CustomRole2
  • Moderators (comments)
  • Moderators (forums)

See anything missing? That’s right, none of the system roles are there! Here’s the list of Sitefinity roles in the backend of my new site:

Sitefinity Roles

The roles clearly exist (and have to, being built-in functions for Sitefinity), so where are they?

Accessing All Built-In Sitefinity Roles

In actuality, what Sitefinity is doing behind the scenes is storing these built-in roles using a different provider. I won’t get too in depth here about the Sitefinity provider model, but in essence it’s a separate data source (which can use the same database, or something completely different). Sitefinity’s Managers, when retrieving them via the GetManager() call, can be fed a provider name. By default (or if you pass in null or empty string), Sitefinity falls back on the default provider name. Most of the time you don’t have to concern yourself with calling out a specific provider, but in this instance we do.

Modify the above line of code to pass in “AppRoles” as the provider for the RoleManager. This gets you access to the other Sitefinity roles that were not included in the first snippet.

RoleManager.GetManager("AppRoles").GetRoleNames()

“AppRoles” is the provider name in this instance. Now, the list of roles we get is the following:

  • Administrators
  • Anonymous
  • Authenticated
  • Authors
  • BackendUsers
  • Designers
  • Editors
  • Everyone
  • Owner
  • Users

(NOTE: Even roles you can’t see listed in the backend, Anonymous/Authenticated/Owner, are returned here.)

In our example scenario, we want to list all our roles, however, not just one subset or the other. Now that we have access to both, it’s as simple as creating a list, and adding both sets together. We then sort the list alphabetically so that the final list we show is nice and organized.

List<string> allRoleNames = new List<string>();
allRoleNames.AddRange(RoleManager.GetManager().GetRoleNames());
allRoleNames.AddRange(RoleManager.GetManager("AppRoles").GetRoleNames());
allRoleNames = allRoleNames.OrderBy(str => str).ToList();

There’s more you can do with access to all the roles than just listing them. You can better-manage a user’s roles programmatically now! With all of the roles at your disposal, you can have business logic set up in code that can add users to some of Sitefinity’s built-in roles, search for users in a given role, or do anything else your business rules require.

The post Accessing Sitefinity Roles in Code appeared first on Falafel Software Blog.

Using HandleUnknownAction in MVC Widgets

$
0
0

Sitefinity’s MVC widgets are fantastic. They allow untold amounts of customization and enable you to create an MVC framework as complex or as simple as you need. There is a caveat to using them, however: Because the controllers are typical ASP.NET MVC controllers, going to particular pages on your site might make them disappear! There is a simple workaround for this problem, and with this information you can ensure that your Sitefinity MVC widgets consistently display no matter what current state your web site is in.

The Problem

Say you have a simple custom MVC widget that has a single “Index” action. Its job is to always render the same “Default” view, regardless of what else is going on on the currently-viewed Sitefinity page. A common example of a widget like this would be for a custom navigation widget. Regardless of what the user is doing, they should always see the site’s navigation. A simple example would look something like this:

using System.Collections.Generic;
using System.Web.Mvc;
using Telerik.Sitefinity.Mvc;

namespace SitefinityWebApp.Mvc.Controllers
{
    [ControllerToolboxItem(Name = "SimpleNavWidget", Title = "Simple Nav Widget", SectionName = "MvcWidgets")]
    public class SimpleNavWidgetController : Controller
    {
        public ActionResult Index()
        {
            var navItems = new List<string> {"One", "Two", "Three"};

            return View("Default", navItems);
        }
    }
}

I created a bare-bones site and put this widget on a page, along with a stock Sitefinity News widget:

So far, so good. But watch what happens when we click to read a particular news story:

Our custom widget vanished! And worse: There is no indication that anything went wrong. There are no logged errors or warnings or traces, and the page itself shows no signs that anything has gone awry. Our widget is simply gone.

The Cause

What’s happening here is default MVC behavior: Our MVC widget sees the page we’re on, and sees that there is additional data in the URL. Most Sitefinity content is accessed using friendly-looking URLs (such as our news story details URL of /news/2016/07/11/example-news-title). The problem is that our widget is reading the rest of this URL, and is misinterpreting what the Action should be for our controller.

The Fix

Luckily, the fix to this problem for these kinds of widgets is simple. All actions that aren’t “Index” in our controller are considered unknown, as they have no defined methods to call. So we’re going to manipulate how our controller handles unknown actions, telling the controller to always call our Index action regardless of what action is given.

[ControllerToolboxItem(Name = "SimpleNavWidget", Title = "Simple Nav Widget", SectionName = "MvcWidgets")]
    public class SimpleNavWidgetController : Controller
    {
        public ActionResult Index()
        {
            var navItems = new List<string> {"One", "Two", "Three"};

            return View("Default", navItems);
        }

        protected override void HandleUnknownAction(string actionName)
        {
            if (!ActionInvoker.InvokeAction(ControllerContext, "Index"))
            {
                base.HandleUnknownAction(actionName);
            }
        }
    }

The method in question, “HandleUnknownAction”, is actually defined on the base Controller class and can be overridden. In this instance, we are simply instructing our controller to say “always invoke the Index action of our controller. If this invoke fails, handle the unknown action like you normally would.” This way, no matter what URL Sitefinity throws at our pages, our custom widget will always render what it’s supposed to render.

If we step into the debugger we can see exactly what action name our controller is receiving when loading the news details page:

02-debug

Since our controller doesn’t have an action to handle the given URL, or the “2016” action, the “HandleUnknownAction” method is invoked. But now we’re handling this scenario, and are manually invoking the “Index” action. Now when the page loads, it correctly renders our widget alongside the details of the news story:

Our widget now functions completely as expected. With the “HandleUnknownAction” utility, you can also define other special cases for your Sitefinity widgets and pages, instead of just always rendering the Index action. This method gives you a lot more control over how your Sitefinity MVC widgets react and render when put into specific scenarios, and you can use this to control them without resorting to hacks or other manual means of achieving your goals.

The post Using HandleUnknownAction in MVC Widgets appeared first on Falafel Software Blog.

Locks in Sitefinity: Keeping Content Free

$
0
0

Exclusive locks are nothing new. It is a basic concept that tons of multi-user programs, tools, and systems use so that users don’t step on each other’s toes when they’re trying to work on something. It would be awful if you spend 30 minutes editing an article only to find that someone else stepped in and blew away your edits because they saw a typo and jumped in to quickly fix it. Sitefinity is no different in this regard: Pages, built-in content, and custom content all have built-in locking functionality. If you’ve worked with other content editors on a Sitefinity site in the past, you’ve probably run across this message when attempting to open something up for editing:

locked

This is a great, simple way to inform you that someone else is currently working on something and the content is otherwise unavailable for edit at the moment. However, that might not end up being the case all the time in Sitefinity. Due to the method of implementation, the above image could actually be displaying incorrect information about the current state of the content item: Perhaps that user actually stopped working on the content hours ago and has long since signed off. Let’s explore what Sitefinity is up to, how to work around it, and how to prevent this scenario from happening in the first place.

The Problem

The biggest issue regarding locks in Sitefinity is that locks never expire. If I go in to edit a Page, for instance, then I get pulled away to work on another task, or I inadvertently close my browser, Sitefinity will happily keep that Page locked under my name indefinitely. Sitefinity does this because the lock guarantees that your current Temp copy (using Sitefinity content lifecycle terminology) of the Page is only preserved for as long as you are the only one editing the Page. If someone were to come in and edit it, my Temp copy would get blown away.

At face value this makes sense: If my computer loses power or my browser crashes, I definitely do not want Sitefinity to instantly release the lock and lose my changes. But if I close my browser without realizing the edit is still open, and go about my day, that content is staying locked and there’s no value to preventing others from accessing it. Sitefinity will happily close it off from everyone else forever. This is not good! While Sitefinity will let me back in to the Page to continue editing without a fuss, other users will be greeted with a Locked message:

Content Locked

Sometimes this can even happen by accident. If I close my browser/tab thinking the content will get unlocked simply by leaving the session, I would be gravely mistaken! So what can we do if I’m out of office saving the world and rescuing the President, therefore having no time to log into Sitefinity to undo my locks?

The Nuclear Option (Administrators Only)

Users with higher privileges like Administrators in Sitefinity see the world a bit differently. When they run into locked content, they actually have a skeleton key at their disposal.

Admin Unlock Option One

Admin Unlock Option Two

An administrator can, at any time, unlock any locked content to make it available. This option should not be taken unless you have confirmation from the user locking the content or if you absolutely have to make an edit. Forcibly unlocking content will permanently destroy the Temp copy that user was working on. Much like the intended scenario described above, unlocking in this manner eliminates the Temp copy, restoring the content to its original state. It is good that this skeleton key is available, but it must be used wisely, or else you’ll have some angry content editors to deal with!

A Better Way: Using Best Practices when Working with Content

The optimal solution to this is actually to teach proper editing etiquette when working in Sitefinity. Locks should only be seen as “someone is presently actively working on this content”, as opposed to the potential “someone left the door locked and escaped through the window, so there’s no way in” scenario that Sitefinity locks enable.

The following tips should be followed when editing Sitefinity content, so that locks are always meaningful, and nobody is left permanently locked out. The basic solution is to always use the navigation options presented within Sitefinity itself, as opposed to using your browser to accomplish the task. Detailed explanations follow below.

If you wish to abandon your changes, then you must exit the page properly, in order to communicate to Sitefinity that you no longer wish to have the content locked. To do so, click the red link at the top of the Sitefinity page when editing, which typically reads “Back to {Content Type}.” This safely backs you out of the currently-editing content, destroys your Temp copy, and frees the content up for editing by other users.

If you wish to save your changes to work on them later, you’ll want to click the “Save as Draft” button, again available at the top of the page. This will change the visible status of the content from “Locked by ” to “Draft (newer than Published).” Your Temp copy is transferred to a Draft copy, which is non-volatile and safe from automatic deletion. Not only does this free the current version of the content up for edits by others (should the need arise), but it also clearly informs other users that you have saved changes, but are not currently working on the content in question.

Always keep in mind: Never use your browser’s back button, or close your active session/browser/tab! This circumvents any methods Sitefinity has built in to prevent locks from being created indefinitely, and leads to the confusing/blocking scenarios described earlier in this post. Sitefinity is generally good at giving you an alert or warning when attempting to use these, and they should be heeded!

Summary

Sitefinity locks serve the same purpose that other system exclusive locks do and help prevent changes from getting lost, but due to their indefinite implementation can lead to some frustrating scenarios. Administrators can brute force their way past locks, but that comes at the cost of losing the locked content changes. Arming content editors and all Sitefinity backend users with the knowledge of how to properly destroy their own unwanted changes, or save their changes for later, can help alleviate and avoid these situations altogether. Using the navigation options and tools within Sitefinity should always be done in lieu of closing your browser or using the back button.

Good luck, and happy content editing!

The post Locks in Sitefinity: Keeping Content Free appeared first on Falafel Software Blog.

Viewing all 73 articles
Browse latest View live