The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.
We previously looked at how you can use Sitefinity's support for External Widget Templates to add support for shortcodes in Sitefinity content by building a simple YouTube shortcode helper button.
In our last example today, we'll take things one step further and create a Sitefinity Blog Post shortcode helper. The idea is to allow users to easily link to any blog post in a content block (or other content item), complete with a user-friendly selector.
Before you begin, be sure that you have setup a default page for your blog, as described in step 5 of the documentation here: Creating A Blog.
This is crucial, as it is this default page property we will use to generate the Url to the specific blog post later in this example.
Next, we need to create a selector dialog to show the list of blog posts to be selected. This can be accomplished easily by adding a simple web form (aspx) that will be shown when the user clicks the Blog Post Shortcode button.
We can use any element to show the blog post list, such as a dropdown menu or radio button list, but because of the rich client-side API of the Telerik AJAX controls, which are of course, included with Sitefinity, we'll go ahead and use the RadListBox.
Add a new web form to your project, and add the following markup and script.
<form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server" /> <h2>Select a blog post</h2> <telerik:RadListBox ID="BlogPostsList" runat="server" DataTextField="Title" DataValueField="Id" /> <input type="button" onclick="selectAndClose()" value="Insert" /> <input type="button" onclick="closeWindow()" value="Cancel" /> <script type="text/javascript"> // helper method to get the RadWindow container function getRadWindow() { if (window.radWindow) { return window.radWindow; } if (window.frameElement && window.frameElement.radWindow) { return window.frameElement.radWindow; } return null; } // fires when the Insert button is clicked function selectAndClose() { // get the RadListBox var listbox = $find("<%= BlogPostsList.ClientID %>"); // retrieve the selected item var item = listbox.get_selectedItem(); // pass the selected Guid back to the widget via the close argument getRadWindow().close(item.get_value()); } // fires when the Cancel button is clicked function closeWindow() { getRadWindow().close(null); } </script> </form>
The RadListBox is eventually going to be bound to a list of Blog Posts, and therefore has it's Text and Value properties set to bind to the Title and Id respectively. The script simply returns the selected value (the Blog Post Id) back to the calling Content Block widget so it can be pasted into the editor later in this post.
Next, in the code-behind, we simply use the Sitefinity API to bind the RadListBox to a list of Blog Posts. In this simple example, I'm just grabbing all the posts in the system on Page Load and binding them to the RadListBox.
protected void Page_Load(object sender, EventArgs e) { using (var api = App.WorkWith()) { var blogPosts = api.BlogPosts().Published().OrderBy(p => p.Title).Get(); BlogPostsList.DataSource = blogPosts; BlogPostsList.DataBind(); } }
A more real-world scenario might have additional controls to select from different blogs, or filter by author, but the basic principle is the same: the RadListBox will show a list of titles, which will then return the Id to the Content Block.
We already looked at how to add custom items to the RadEditor toolbar, so all we need to do now is define the behavior that will show our dialog, and insert the shortcode when selected.
Add the following script to the HtmlField template.
<script type="text/javascript"> Telerik.Web.UI.Editor.CommandList["BlogPost"] = function (commandName, editor, args) { var myCallbackFunction = function (sender, args) { if (args == null) return; editor.pasteHtml(String.format("[blogpost:{0}]", args)) } editor.showExternalDialog( '/Templates/BlogPostSelector.aspx', null, 270, 300, myCallbackFunction, null, 'Insert Blog Post', true, Telerik.Web.UI.WindowBehaviors.Close + Telerik.Web.UI.WindowBehaviors.Move, false, false); }; </script>
Calling editor.ShowExternalDialog is all we need to do to show our custom page in the built-in RadWindow for the editor. For specific details about the arguments of this method, be sure to see the full documentation: Add Custom Dialogs.
The key argument is the first Url parameter, which identifies the path to our custom web form, and the fifth callback parameter, which points to a callback function that should execute when the dialog closes.
This method is defined above, and simply takes the argument (which as explained previously, contains the blog post Id) and creates a shortcode, pasting it into the editor.
The last item we need to define is the helper method to expand the blog post shortcode. For more on the shortcode expander helper methods, see the previous article Supporting Shortcodes in Sitefinity Content.
Add a new class to your project with the following static Expand method as shown here.
public static string Expand(object inputText) { if (inputText == null) return string.Empty; var result = inputText.ToString(); // use regex to find matches in the text string regex__1 = @"\[blogpost:.*?\]"; MatchCollection matches = Regex.Matches(result, regex__1); // open the content and page managers var mgr = BlogsManager.GetManager(); var pageMgr = PageManager.GetManager(); // parse through all shortcode matches in the text foreach (Match match in matches) { // extract the blog post id var blogPostId = new Guid(match.Value.Substring(10, match.Value.Length - 10 - 1)); // find the matching blog post (if any) var blogPost = mgr.GetBlogPosts().FirstOrDefault(p => p.Id == blogPostId && p.Status == ContentLifecycleStatus.Live); if (blogPost == null) continue; // get parent blog var blog = blogPost.Parent; if (blog == null) continue; // get parent blog default url var defaultPage = blog.DefaultPageId; if (!defaultPage.HasValue) continue; var page = pageMgr.GetPageNode(defaultPage.Value); if (page == null) continue; // get blog post url var pageUrl = page.GetFullUrl(); var fullUrl = string.Format("{0}{1}", VirtualPathUtility.ToAbsolute(pageUrl), blogPost.Urls.Where(u => u.RedirectToDefault == false).First().Url); // replace shortcode with link to blog post string player = string.Format(@"<a href=""{0}"">{1}</a>", fullUrl, blogPost.Title); result = result.Replace(match.Value, player); } return result; }
As the code shows, we first need to find the blog that matches the given Id, then use its default page property to build the Url. Finally, we use the default blog post Url to create the full link to the details view for the post and return it to the Content Block.
The result is a complete link to the blog post, complete with the actual post title, using only the simple blog post shortcode.
Sitefinity's support for external widget templates is a powerful way to add custom functionality to your website. Combining this with the intuitive API unlocks unlimited possibilities for extensibility and customization. Try if for yourself today and be sure to share your customizations, as well as comments and suggestions with us in the Sitefinity discussion forums.
View all posts from The Progress Team on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.
Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.
Learn MoreSubscribe to get all the news, info and tutorials you need to build better business apps and sites
Progress collects the Personal Information set out in our Privacy Policy and the Supplemental Privacy notice for residents of California and other US States and uses it for the purposes stated in that policy.
You can also ask us not to share your Personal Information to third parties here: Do Not Sell or Share My Info
We see that you have already chosen to receive marketing materials from us. If you wish to change this at any time you may do so by clicking here.
Thank you for your continued interest in Progress. Based on either your previous activity on our websites or our ongoing relationship, we will keep you updated on our products, solutions, services, company news and events. If you decide that you want to be removed from our mailing lists at any time, you can change your contact preferences by clicking here.