Orchard CMS Up and Running

Orchard CMS: Up and Running John Zablocki Beijing • Cambridge • Farnham • Köln • Sebastopol • Tokyo Orchard CMS: Up and Running by John Zablocki Copyright © 2012 John Zablocki. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://my.safaribooksonline.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Editor: Rachel Roumeliotis Production Editor: Melanie Yarbrough Proofreader: Melanie Yarbrough Cover Designer: Karen Montgomery Interior Designer: David Futato Illustrator: Robert Romano Revision History for the First Edition: 2012-05-24 First release See http://oreilly.com/catalog/errata.csp?isbn=9781449320218 for release details. Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of O’Reilly Media, Inc. Orchard CMS: Up and Running, the cover image of a Brush-tailed Bettong, and related trade dress are trademarks of O’Reilly Media, Inc. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O’Reilly Media, Inc., was aware of a trademark claim, the designations have been printed in caps or initial caps. While every precaution has been taken in the preparation of this book, the publisher and authors assume no responsibility for errors or omissions, or for damages resulting from the use of the information con- tained herein. ISBN: 978-1-449-32021-8 [LSI] 1337864455 To Lady and MaryKatherine Table of Contents Preface ..................................................................... ix 1. Getting Started with Orchard ............................................. 1 Prerequisites 1 Development Environment 1 Obtaining the Orchard Solution 2 Packaged Releases 2 Using Source Control 2 The Contents of the Solution 4 Setting Up the Website 4 Configuring Orchard for the First Time 4 The Orchard Dashboard 7 Creating Content 7 Adding Widgets 10 Orchard Modules 12 Orchard Security 13 Summary 14 2. Creating and Managing Content .......................................... 15 Bio Items 15 Content Types 16 Content Parts 16 Content Fields 18 Projections and Queries 20 Filters 21 Sort Criteria 21 Layouts 21 Event Items 22 Content Types and Fields Continued 22 Projections and Queries Continued 24 Daisy’s Blog 25 v The Blog Module 26 About Page 27 Contact Page 28 Home Page 30 Zones and Layers 31 Gallery 32 Summary 34 3. Displaying Content ..................................................... 35 Customizing Biography Content 35 Content Templates 36 Alternate Templates 37 Customizing Events 41 Placement Files 41 Field Templates 42 Shape Tracing 43 Summary 47 4. Creating Themes ....................................................... 49 The Orchard Command-Line Interface 49 Getting Help 49 Code Generation Tools 50 Code Generating a Theme 51 The Structure of a Theme 52 Default Content Templates 53 Working with Views 54 Layouts 55 Zones and Layers 56 Alternate Templates 57 Theme Inheritance 57 Basic Styling 60 Styling Projections 61 Shape Wrapping 62 Theme Metadata 63 Theme Previews 63 Theme Credits 63 Summary 64 5. Creating Modules ...................................................... 65 The Places Field 65 Getting Places Data 66 Module Code Generation 66 The Places Field Project 67 vi | Table of Contents Places Field Model 67 Drivers 69 Field Templates 72 Settings 75 Controllers 77 Module Metadata 80 Using the Places Field 80 Creating Content with the Places Field 80 Displaying the Places Field 83 Summary 84 6. Creating Widgets ...................................................... 85 Content Parts 85 The JW Player Widget 85 Creating the Module 86 JW Player Model 87 Database Migrations 88 Handlers and Drivers 90 Placement 91 Enabling Our Module 92 A Second Migration 92 The Widget Views 93 Adding the Widget to a Zone 95 Widget Metadata 96 Summary 98 7. Localization ........................................................... 99 Orchard Site Localization 99 Admin Localization 99 PO Files 101 Content Item Localization 102 Summary 104 8. Maintaining Orchard Sites .............................................. 105 Packaging Themes and Modules 105 Deploying Orchard Sites 107 Shared Hosting 107 Dedicated Hosting 108 Cloud Hosting 108 Multi-Tenancy 108 Modules and Performance 108 Workflow 109 Upgrading 109 Table of Contents | vii Summary 110 9. Conclusion ........................................................... 111 viii | Table of Contents Preface A few months back, I wanted to create an online presence for my band, Daisy’s Gone. In the past, I would have started from scratch. But I remembered all the domains I’ve accumulated over the years that are now nothing more than parking pages at my reg- istrar. It’s generally not hard to throw together a few simple brochure-ware pages. I certainly could have done just that for my band’s site. However, even simple sites often have content and behavior that needs to be dynamic. The obvious solution is to use a content management system (CMS). CMS platforms such as WordPress and Drupal offer users prebuilt functionality for everything from creating pages to managing site registration. A full-blown CMS will allow non-technical users to create and manage content and will allow programmers and designers to extend the out-of-the-box functionality by creating themes and modules, respectively. With Daisy’s Gone, I was once again presented with the dilemma of whether to build a site from scratch or to use a CMS. Fortunately, I remembered how I had seen a then just-released Orchard CMS used at the NYC Give Camp a few months earlier. Give Camps match developers and designers with charities who have some unmet technical need, often a web presence. One of the developer groups built its charity a new website using Orchard. I remember being quite impressed by how much this team accomplished in one week- end using this new CMS. So when it came time to start building the website for my band, I made the choice to use Orchard. The original Daisy’s Gone website was not much more than a home page. It was pri- marily a sandbox for me to learn Orchard. As I write this book, I’m going to create a new online presence for the band. I’ll walk through the steps of creating a custom look and feel (themes) and extending Orchard with new functionality (modules). Whether you are building a new corporate site for your company or a site for the local youth soccer league, you are likely to have many of the same needs of your CMS. Building a site for a band is no different. You may need to schedule events, manage user comments, support OAuth, or have a site map. The content will vary by your domain, the features much less so. ix About the Orchard Project Microsoft released Orchard in January 2011, along with ASP.NET MVC 3, IIS Express, SQL CE 4, Web Farm Framework, and WebMatrix. What all of these technologies have in common is that they, in some way, aim to make web development on Windows more accessible. PHP owes its wide adoption to its perceived ease of use. With this new tool chain, Microsoft is courting the community who wants a simplified development experience. The Orchard Project belongs to the Microsoft supported Outercurve Foundation (for- merly known as the CodePlex Foundation). The Outercurve Foundation is a nonprofit organization whose mission is to foster support for open source projects, such as Or- chard and the popular ASP.NET toolkit, MVC Contrib. Microsoft does not officially support Orchard, but its employees are currently among those leading the development efforts for the project. There are three stated goals of the Orchard Project. The first is to provide a set of shared components to be used in ASP.NET applications. The second is to create a set of ref- erence applications built using these components. The third is to build a community to support these components and applications. At the time of this writing, Orchard is primarily a platform for creating content-driven websites. Though in Orchard, content is certainly not limited to blog posts or simple text-heavy pages. Orchard is developed with a full open source stack. It uses ASP.NET MVC 3.0 with the Razor view engine. There are also numerous dependencies on other open source projects, most notably NHibernate for data access and Autofac for dependency injec- tion. Orchard is licensed under the BSD license. Not much more than a year after its 1.0 release, Orchard has been downloaded nearly 1,000,000 times. More than 300 modules and themes have been created and submitted to the Orchard Gallery. A series of minor releases have taken Orchard to its current 1.4 version. The Orchard community is strong and growing. Why Another .NET CMS? There have been several commercial and open source .NET CMS products over the years. DotNetNuke (DNN) is arguably the most notable and most popular. However, it was written in VB.NET and remained that way until earlier this year when it was ported to C#. VB.NET was a deal-breaker for me, as it was for many developers. Even though DNN is a C# project now—as is another popular open source .NET CMS, Umbraco—both are WebForms projects. Like VB.NET, WebForms is also a deal- breaker for me. While the underlying web framework or programming language used by a CMS is of little consequence to an end user, to a programmer it will likely be important. As an MVC developer, I’ve wanted a CMS that is built on ASP.NET MVC and uses metaphors that are familiar to MVC development. I believe the .NET x | Preface developer community will gravitate towards Orchard, because its development stack is more in line with modern ASP.NET development practices. That said, it is entirely possible to build content and functionality-rich sites using Or- chard without having to write a single line of code. Even though Orchard is a relatively new product, it has a remarkably rich set of extensions. Like other modern CMS prod- ucts, Orchard marries great programmatic extensibility with rich, out-of-the-box functionality. Audience This book is being written for web developers who want to create content-heavy web- sites without starting from a blank slate. I assume readers have some familiarity with basic CMS concepts and some level of web development skill. Experience with Orchard is neither assumed nor required. Programming custom modules for Orchard does re- quire knowledge of C# and to a lesser extent ASP.NET MVC. If you are unfamiliar with MVC, I suggest reading 20 Recipes for Programming MVC 3 or Programming ASP.NET MVC 4 (O’Reilly) or Programming Microsoft ASP.NET MVC (Microsoft Press). Many Orchard users will be able to accomplish their goals without having to write any custom code. This book will fully cover how to get a site up and running with Orchard and will walk readers through customization with existing themes and sites from the Orchard Gallery. However, even users who will not be building custom modules might benefit from the chapters describing module and theme architecture. Knowing how Orchard works will help users debug problems that might arise in production. Contents of This Book This book introduces Orchard development by walking a reader through the process of creating a customized Orchard site. Chapter 1 covers getting an Orchard site set up for development and provides a quick tour of the Orchard experience. Chapter 2 details the process of creating content from existing types of content and covers how to create new types of content. Chapter 3 describes how to change the way Orchard displays content by default. Chapter 4 explores the process of customizing an Orchard site’s look and feel by build- ing a theme. Chapter 5 provides a walkthrough of creating and configuring a custom module. Chapter 6 provides a walkthrough of creating and configuring a custom widget. Chapter 7 explores options for adding multi-language support to an Orchard site. Preface | xi Chapter 8 discusses considerations for maintaining Orchard sites. Chapter 9 contains the final thoughts on what is covered in this book. Companion Material All code samples in this book are available on Bitbucket in my OrchardCMS repository located at https://bitbucket.org/johnzablocki/orchardcms. I use Mercurial for version control. Any general purpose module created in this book will also be available for download in the Orchard Gallery. And of course, you will be able to view the finished project at http://www.daisysgone.com. Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values deter- mined by context. This icon signifies a tip, suggestion, or general note. This icon indicates a warning or caution. Using Code Examples This book is here to help you get your job done. In general, you may use the code in this book in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require xii | Preface permission. Selling or distributing a CD-ROM of examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “Orchard CMS: Up and Running by John Zablocki (O’Reilly). Copyright 2012 John Zablocki, 978-1-449-32021-8.” If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at permissions@oreilly.com. Safari® Books Online Safari Books Online (www.safaribooksonline.com) is an on-demand digital library that delivers expert content in both book and video form from the world’s leading authors in technology and business. Technology professionals, software developers, web designers, and business and cre- ative professionals use Safari Books Online as their primary resource for research, problem solving, learning, and certification training. Safari Books Online offers a range of product mixes and pricing programs for organi- zations, government agencies, and individuals. Subscribers have access to thousands of books, training videos, and prepublication manuscripts in one fully searchable da- tabase from publishers like O’Reilly Media, Prentice Hall Professional, Addison-Wesley Professional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, John Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, Adobe Press, FT Press, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett, Course Tech- nology, and dozens more. For more information about Safari Books Online, please visit us online. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-998-9938 (in the United States or Canada) 707-829-0515 (international or local) 707-829-0104 (fax) We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at: http://oreil.ly/orchardcms_upandrunning Preface | xiii To comment or ask technical questions about this book, send email to: bookquestions@oreilly.com For more information about our books, courses, conferences, and news, see our website at http://www.oreilly.com. Find us on Facebook: http://facebook.com/oreilly Follow us on Twitter: http://twitter.com/oreillymedia Watch us on YouTube: http://www.youtube.com/oreillymedia Acknowledgments While a team of developers is able to build Orchard full-time, the rest of the Orchard ecosystem comes from the work of the community. I thank these volunteers who leave blog posts and code for Bing to find and the rest of us to follow. Without their efforts, there would not be an Orchard about which to write this book. I’d like to thank my editor, Rachel Roumeliotis, for giving me the opportunity to write this book. Rachel has been great to work with from the beginning. She understood when a demanding new job slowed my writing pace and when a new release of Orchard invalidated nearly half of my work. I hope to be able to work again with Rachel. I’d also like to thank O’Reilly Media and its employees who helped get this book pub- lished. I realize I am just one piece of many in the puzzle that is turning a bunch of Word documents in a Dropbox folder into a printed work. Finally, I am forever grateful and thankful to my technical reviewer, Mark Freedman. Mark has long been a colleague and a mentor. He’s helped me not only with this book, but with so many aspects of my career. I consider myself fortunate to have started my career many years ago under Mark’s management. Twelve years later, Mark’s guidance has again proved invaluable. I couldn’t have written this book without him. xiv | Preface CHAPTER 1 Getting Started with Orchard We’re about to start building an Orchard website. We’ll create some content. We’ll manage some content. We’ll change the way our site looks and behaves. We’ll write some code to extend the functionality that’s available out of the box. Though we could perform all of these tasks without ever looking at the Orchard source, we’re .NET developers. We’re most comfortable in Visual Studio, so why wouldn’t we start there? Prerequisites Though it’s not entirely necessary, it’s my preference to build modules, create themes, and manage my Orchard sites all within the context of the full Visual Studio 2010 Orchard solution. Aside from being able to debug the site with Visual Studio, having the source handy also provides a great reference when creating your own Orchard extensions. We’ll learn how to develop extensions in the chapters ahead. Development Environment Orchard extensions are known as modules. Creating a module requires writing code, typically C#, but any .NET language will work. You could write that code in Notepad or any text editor of your choice, but that wouldn’t be the most efficient way to work. In this chapter and those that follow, I assume that you’ll be working with Visual Studio 2010 Professional or higher. The Orchard documentation contains tutorials on how to perform tasks, such as cre- ating modules, without Visual Studio. While it certainly is possible to do so, it’s im- practical to consider this approach for all but the most basic of Orchard workflows. You could use the Express editions of Visual C# and Visual Web Developer. However, you won’t be able to open the master solution. Express editions don’t support mixed project type (web and class library) solutions. 1 A $500 IDE might seem to be an expensive barrier to entry for an open source project such as Orchard. To lower the bar, the Orchard team has provided documentation and tooling that supports non-Visual Studio workflows. The open source IDE MonoDevelop is probably the best alternative, as you can work within the master Orchard solution. Obtaining the Orchard Solution There are two ways to get the Orchard solution onto your local development environ- ment. The simplest way is to go to the Orchard downloads at CodePlex.com, which is Microsoft’s open source project hosting site. Though I sometimes use the source con- trol option described below for my Orchard development, I’m instead going to use a packaged release for this book. That will guarantee we’re all using the same source. The solution I’m using for this book can be found at http://orchard.codeplex.com/relea ses/view/74491. Packaged Releases With each release (major or minor), Orchard zip files are made available on this page. One is a precompiled version of Orchard, which is optimized for users who want to deploy Orchard and won’t be coding extensions for the site. This package is labeled Orchard.Web.1.4.0.zip. At the time of this writing, 1.4.0 is the current version. This ZIP file appears under the heading “Recommended Download.” The URL http://orchard.codeplex.com/releases/view/74491 will always point to the 1.4.0 release. Click the “Downloads” tab on the page to find downloads for the latest version as it may have changed by the time this book is published. A second ZIP file download is a snapshot of the Orchard source code as it existed for the release. This package is labeled Orchard.Source.1.4.0.zip. Download this zip file and extract it to work with the most recent and stable release of Orchard. This ZIP file appears under the heading “Other Available Downloads.” Each release will have dif- ferent zip files available, but the web and source downloads should be consistently available. Using Source Control As an alternative, you could get the Orchard source by cloning the source control repository. This is the method I prefer for module development, as it guarantees that I will be developing against the most recent committed changes. Of course, there is a risk that a feature in the current codebase will not make the final cut. For this reason, to follow along with the examples in this book you should download the source as 2 | Chapter 1: Getting Started with Orchard described previously in this chapter. However, I’ll describe how to clone the source later in case you wish to try that option instead. The Orchard team uses the distributed version control system (DVCS) Mercurial. You don’t need to understand much about Mercurial (Hg) in order to use it to get the Orchard source. I won’t cover anything beyond where to download Mercurial and the two commands you’ll use with Mercurial and Orchard. If you would like more infor- mation on Mercurial, I suggest reading Mercurial: The Definitive Guide by Bryan O’Sullivan (O’Reilly). Mercurial is an open source project and is freely available at http://mercurial.selenic .com/. There is a Windows shell extension called TortoiseHg, which allows you to access common commands by right-clicking on folders in Windows Explorer. When you download and install TortoiseHg, the command line client is also installed. After running the installer, open up a command prompt and enter the following command: hg clone https://hg01.codeplex.com/orchard Orchard The clone command in Mercurial is loosely analogous to a checkout command in Sub- version or Team Foundation Server. One significant difference is that cloning copies the entire repository locally. With a DVCS such as Mercurial, there is no centralized repository. The first argument passed to the clone command is the URL where the repository may be found. The second is the name of the local directory to where your repository will be copied. This path is relative to the directory from which you ran the hg command. In other words, if the command prompt opened into C:\users\John, you’d have a new folder: C:\users\John\Orchard. If the hg command was not recognized by Windows, it is likely that the path to hg.exe was not added to your system’s path environment vari- able. By default this path is C:\Program Files (x86)\Mercurial. If you choose to obtain the source by way of cloning the repository, it’s a good practice to get the latest changesets, or commits, to the repository. Open a command prompt to the directory into which you cloned Orchard and run the following command: hg pull The pull command will get the latest version of the Orchard sources. However, Mer- curial doesn’t by default add updates into your working copy. To update your local repository with any changes found during a pull, run the following command: hg update Obtaining the Orchard Solution | 3 The Contents of the Solution If you navigate to the directory where you either unzipped the source package or cloned the Mercurial repository, you should see two directories src and lib. Open up the src directory and locate the file Orchard.sln, which is the Orchard solution. Open this solution file and build it. Setting Up the Website After you’ve successfully compiled the Orchard source, you’ll need to create a virtual directory in Internet Information Services (IIS). Name this virtual directory “Orchard.” The physical path of the virtual directory should be set to the location of the Orchard web project. If you copied the Orchard source to a directory c:\dev\Orchard, then the physical path would be c:\dev\Orchard\src\Orchard.Web. Alternatively, you could simply use the ASP.NET Development Server that ships with Visual Studio. In fact, the default settings of the Orchard solution are to run the Orchard.Web project using this server. While, generally speaking, the development server should be adequate for the purpose of Orchard site development, I prefer to work with IIS when possible. IIS will be what you use in production, so you’re more likely to catch certain issues in development with a local IIS setup. After you’ve created the virtual directory, open up a web browser and navigate to http: //localhost/orchard. If you are using the ASP.NET Development Server, simply run the Orchard.Web project using Ctrl+F5 to start the site without debugging (or just F5 to start with debugging). You should see the screen in Figure 1-1. The very first run of an Orchard site will probably seem slow as some of the dynamic components are compiled and cached. Configuring Orchard for the First Time The Get Started page (Figure 1-1) requires you to provide a few quick details to get your Orchard site up and running. The first three questions ask you to name your site and provide an administrative username and password. These values should be straightforward. You then have the option to select a SQL Server Compact or full SQL Server (or SQL Express) database. Typically you’ll choose SQL Server Compact for development only, but a low traffic site might make do with a SQL Server Compact database. Create your new site with the name “Daisy’s Gone.” Revisit the Preface to remind yourself of the purpose of our site. You should also change the default admin name to something identifiable, like “jzablocki.” Make this change so that when content, such 4 | Chapter 1: Getting Started with Orchard as blog posts or event listings, is added to the site, the name of the author isn’t “admin.” We’ll also opt to use SQL Server Compact for storage since we’re setting up this site for development purposes. Figure 1-1. The Get Started page of Orchard CMS Configuring Orchard for the First Time | 5 An advantage of using SQL Server Compact is that you’re easily able to reset an Orchard site back to its starting state (the Get Started page) by deleting the App_Data folder containing the site’s local database. On my system, this path is C:\dev\Orchard\src\Orchard.Web\App_Data \Sites. Resetting the site is useful when you want to ensure a clean slate for new site development. The final question on the Get Started page involves selecting an Orchard recipe. Recipes are preset site configurations and are simply XML files stored in a well-known path. It’s also possible to create your own. The default Orchard setup includes three: Default Includes frequently used Orchard features. This recipe is suitable as a starting point for most sites. Blog Creates an Orchard site to be used as a personal blog. Core Configures Orchard to have only the minimum required features. This recipe is much better for Orchard development than for site creation. Though the “Core” recipe is suitable for module development, we’re going to use “De- fault” since we’re going to be building a fully functional site. After you’ve answered these five questions, click the “Finish Setup” button. You should see a modal progress bar appear with the message “Cooking Orchard Recipe” (see Figure 1-2). Figure 1-2. Cooking an Orchard recipe 6 | Chapter 1: Getting Started with Orchard After the recipe has finished cooking, you’ll be redirected to your site’s home page (Figure 1-3). You’ll see the site name that we entered on the “Get Started” page in the upper-left corner and a series of text sections, each with a title and some content. Figure 1-3. The default Orchard home page The Orchard Dashboard At the very bottom of the home page, you’ll see the default template’s footer with a few links. The last link is labeled “Dashboard.” Click that link to get to the admin pages for your site. When you click through, you’ll land on the admin home page. This page simply has links to finding more information on Orchard. The functionality for man- aging content is accessible via the menu on the left side of the screen. We’re going to explore the admin pages in more detail in the chapters ahead. For now, we’ll just take a quick tour of the basics. Creating Content Start by clicking the “Content” link on the menu. This is the admin page where you’ll manage content, such as your site’s home page (Figure 1-4). There are three tabs on this page, which are described here. (Don’t worry about the new terms in this chapter; we’re going to revisit them over the next few chapters.) The Orchard Dashboard | 7 Content Items Single piece of content, such as a blog post or an event listing. Content Types Blueprints for content items. Defines the attributes and behavior of the content items that you’ll create using the admin tool. Content Parts Reusable pieces of functionality that may be used to compose content types. Figure 1-4. The content management admin page To get a sense of how content is created using these tools, click the Create New Content button in the upper-right corner of the page. You’ll be taken to a page where you are shown two links: Page and Projection. Select Page. You’ll then arrive at the page shown in Figure 1-5. Right now, our site has only the home page. We’re going to use the New Page form to add an About page. For the title, enter “About Daisy’s Gone.” Then add some content describing the band. We’ll also check the option to “Show on main menu,” which will add a tab to the site’s main navigation. Checking this option enables an additional textbox for setting the menu text. Enter “About.” You’ll then have the option to set a “Created On” timestamp. We’ll let this default to the current date and time. We’ll also choose to “Publish Now” rather than select a future date via the “Publish Later” option. The Save button will allow you to save your work without having it appear on the site. After you publish your page, click the Your Site link in the upper-left corner of the admin pages. You’ll be taken to the home page. Notice the new “About” link that has been added to the menu. Your new page is accessible via that tab (Figure 1-6). 8 | Chapter 1: Getting Started with Orchard When working with a CMS such as Orchard, it’s useful to keep one browser tab open to the Dashboard and a second open to your site. This practice will allow you to Ctrl+Tab between tabs to see your changes on the live site without navigating away from your current admin loca- tion. Figure 1-5. The new page admin screen Figure 1-6. The new About page The Orchard Dashboard | 9 Adding Widgets Widgets are UI components that may be added to some or all pages of an Orchard site. Click on the “Widgets” menu item to manage these components. On this page, you’ll see a listing of layers and zones. Layer Set of rules that define when widgets will appear. Zone A placeholder into which widgets may be inserted. In Chapter 4, we’ll learn about zones, layers, and layouts in detail. For now, know that a theme defines a site’s layout and a layout defines which zones are available. The listing of zones you see on the “Widgets” admin page were defined in the default theme, which is called “The Theme Machine.” Layers define rules for which zones will be active. Figure 1-7. Managing widgets 10 | Chapter 1: Getting Started with Orchard Notice that the zones TripelFirst, TripelSecond, and TripelThird have links with the text “First Leader Aside,” “Second Leader Aside,” and “Third Leader Aside,” respec- tively. If you click over to your site’s home page, you’ll see that these are headings on the three zones at the bottom of the page (Figure 1-3). Click through “First Leader Aside” and you’ll find yourself on a page where you can edit the HTML page of this widget. Appropriately, this is called an HTML Widget. If you click the “Add” button next to any zone, you’ll see “HTML Widget” listed as an option for each. As we learn to create and manage content, we’ll go over these options in more detail. For now we’ll take a quick look at changing widgets. Click on the HTML widget under “TripelFirst.” Enter the title “News and Notes.” Click on the bulleted list in the HTML editor and enter some news or some notes. Save the changes and visit your site’s home page again. You should see your new content reflected on the home page (Figure 1-8). Figure 1-8. HTML widget updates The Orchard Dashboard | 11 You might be wondering why the third set of zones are named “Tripel” and not “Triple.” It’s not a misspelling. The zones are a sort of inside joke and a tribute to Tripels, which are a particular style of high alcohol, strong pale ales. Orchard Modules A great deal of functionality is available for an Orchard site by downloading and in- stalling Orchard modules. Clicking on the “Modules” admin menu option at first re- veals the set of installed modules under the “Features” tab. The “Default” recipe we chose to setup our site has influenced this listing, as recipes can instruct the Orchard software to include different modules. We’ll explore the “Features” tab in more detail when we write our first custom module. We want visitors to know where to find the band on rehearsal nights, so we’re going to include an embedded Bing map that will display a push pin at this location. We’ll place this map on the home page only. Bing Maps aren’t out of the box Orchard func- tionality, so we’re either going to have to code a solution or find one that’s already coded for us. Fortunately, the work has already been done. Click on the “Gallery” tab; you’ll see a listing of modules available for download and installation. Search for “Bing Maps.” There will be a few results. The one we want is a module named “Bing.Maps” that was authored by Orchard project lead Bertrand Le Roy. Click “Install” to download and install the module. After it is installed, you’ll be prompted to enable it. After you enable it, you will be able to add it to a zone. At the time of this writing, there is a bug that might lead to a “Package installation failed” error when installing a theme or module from the Gallery. If you get that message along with a note about permissions errors, click Settings→Gallery and set the gallery feed URL to http:// packages.orchardproject.net/FeedService.svc/. Return to the “Widgets” admin page. Change the “Current Layer” drop-down box from “Default” to “TheHomepage.” Locate the zone “TripelThird.” Click “Remove” to delete the existing HTML Widget from that zone. Click “Add” and select the “Bing Map Widget” from the list of possible widgets. Enter the title “Where Daisy’s Gone Practices”; a latitude and longitude of 42.375,-70.983; and width and height of 300×200. Set the “Zoom” level to 10 and click “Save.” If you navigate to the site home page, you should now see a Bing Maps widget where before there was placeholder text (Figure 1-9). 12 | Chapter 1: Getting Started with Orchard Orchard Security We’ve seen how to manage content. Now we’ll look briefly at who can manage content. The “Users” admin feature contains forms for managing users and roles. Creating a user is a relatively straightforward process. Click “Add New User” and enter a user- name, email, and password. You are also able to add users to roles. There are a number of predefined roles that are part of a standard Orchard installation. Administrator Has full control over a site and its content Editor Can edit and publish, but not create content Moderator Can validate user-generated content, such as comments Author Can create and publish content Contributor Can write, but might not be able to publish content Figure 1-9. A Bing Maps widget Orchard Security | 13 There are also roles defined for Anonymous and Authenticated users, but membership is based on whether a user is logged into the site. These roles are not assignable. New roles with custom permissions may also be created. The “Add a role” button is acces- sible via the “Roles” tab. When creating a new role, you select the permissions you want to be available for users in this role. We’re going to create two users in addition to our administrator, who in my case is named “jzablocki.” Click “Add new user” and enter the username “gcocca.” Check the “Editor” and “Author” roles and save the new user. Create another new user with the username “nsilvia” and check the role “Moderator.” Save this user. Greg—gcocca— will now be able to create, edit and publish content. Nino—nsylvia—will be able to moderate user generated comments. Figure 1-10. Orchard user management One additional user setting to be aware of is under the “Settings” section of the admin menu. Click Settings→Users and you’ll find additional user management settings. You can allow users to self-register, reset their own passwords, verify email addresses, and go through an approval process before having authenticated access to the site. We’ll stick with unauthenticated comments for our content and leave all options unchecked. Summary We’ve now seen how to get an Orchard site up and running from the Orchard source. We’ve created and edited content. We’ve downloaded a module from the Orchard Gallery and added it to our home page. A simple site could be built and maintained using what little we’ve learned so far. Our site won’t be too complicated, but it will require some basic customization. In the chapters ahead, we’re going to learn how to unleash the power and extensibility of Orchard. 14 | Chapter 1: Getting Started with Orchard CHAPTER 2 Creating and Managing Content In Chapter 1, we briefly explored how to work with the content management tools of Orchard. In this chapter, we’ll dig a little deeper as we build out a baseline to our site, which will then be extended with custom modules and a custom theme in later chapters. This baseline will include a handful of simple pages as described here. These pages will provide enough variety that we’ll be able to showcase Orchard’s content management capabilities. Home Page The same home page from the previous chapter About The About page from the previous chapter Contact A page with a form to use to contact the band Events A page listing upcoming band-related events Bios A page with band biographies Blog A standard blog Gallery A page with images, videos, and audio Bio Items In its simplest form, a biography on the Daisy’s Gone Orchard site is just a web page. It could easily be added to the site using the same approach we used to add the “About” page in the previous chapter. However, we’re going to have a bio page for each band member. Though we could cram all bios into a single page, it would be more readable 15 to have a unique page for each musician (and drummer). That requirement means that I’ll effectively be creating the same page at least three times, just with different content. An old joke asks, “What do you call the guy who hangs out with the musicians in the band?” and answers “The drummer.” The process of creating a bio page is somewhat analogous to that of creating a blog post. Each blog post has common attributes, such as a title, a body, tags, and comments. When we manage blogs in a CMS, we typically have a special form for creating posts; we don’t just create a new page and lay it out a certain way. Similarly, each bio will have a musician’s name, a date of birth, and a body. We want to create bio pages just as we create blog posts—with a special admin form. Content Types As we saw in the previous chapter, the “Content” admin menu has three tabs. “Content Items” lists the two pages we’ve created so far. “Content Types” lists the different types of content that we can create on our Orchard site. “Content Parts” lists some reusable components that may be added to content types. Click into the “Content Types” tab and click the “Create new type” button. The new content type is going to be a Bio type. Create the new type with this name and leave the content type id as “Bio,” which is what Orchard automatically set the value to when we typed in the name. Then click the “Create” button. Content Parts From the screen that followed, we’re going to add content parts to the Bio content type. By adding these parts, Bio content will automatically gain behavior and attributes. To the Bio type, add the content parts that follow by checking the appropriate boxes and clicking “Save.” Autoroute Makes a bio item accessible by a URL matching a defined route pattern Body Adds an HTML content section to the content item Containable Allows a content item to be contained within another content type Menu Allows a content item to appear on the main navigation Publish Later Allows a content item to be edited and saved, but activated at a future date 16 | Chapter 2: Creating and Managing Content Title Allows a page title to be captured Figure 2-1. Adding content parts to a content type After saving parts to the content type, you’ll be taken to a page where you can choose to add fields or additional parts. For now, just click “Save” at the bottom of the page. We’ll revisit these options later in this chapter. After saving, we now have an option under the “New” menu item called “Bio.” Clicking this option opens a new form where we’ll create new content items for our Bio content type. At this point, our Bio type has only title and body fields where we can enter bio content. We’ll add to these fields shortly. For now, enter the name of the person whose bio is being created for the title and add some biographical content to the body. Before clicking “Publish Now,” we’ll want to make note of the “Permalink” field that’s displayed after the “Title” field. The permalink contains the URL by which our Bio item may be reached. Recall that we added the Autoroute content part to our content type, which will take care of mapping the permalink to the right content item. We didn’t change the defaults for Autoroute, so the title value will be used to complete the URL for this item. With the title “John Zablocki,” our Bio item may be reached at http://localhost/orchard/john-zablocki. Now you can click “Publish Now.” After pub- lishing, browse to the item at the permalink URL. Bio Items | 17 Note that we also included the Menu part and could have chosen to add the bio page to our main menu. We’re going to create a few Bio items, so we don’t want to inflate the navigation. But having the part gives us that option in the future. Figure 2-2. The New Bio admin page Content Fields At this point, the Bio template is a little thin in that it’s lacking fields for capturing information such as instruments played and bio pictures. Let’s first add the field for instruments played by editing the Bio template. Click the “Content” menu option and then click the “Content Types” tab. Clicking the “Edit” option on the “Bio” row opens up the “Edit Content Type” page. Clicking “Add Field” will open a form for creating a new field for the Bio template. Several types of fields are available by default. A text field is sufficient for the instru- ments field, so select that value for “Field Type.” Set the “Display Name”—used for labels—of this field to “Instruments.” Let the “Technical Name”—used in code— default to “Instruments.” Click “Save.” Add two additional text fields with display names of “Birthplace” and “Lives In.” Accept the respective technical name defaults of “Birthplace” and “Lives In.” Clicking New→Bio reveals that the Bio template now has the three additional fields. Similarly, clicking Content→Edit on the Bio content item also shows that these new fields are available for existing Bio content items. Enter “Guitar and Vocals” under “Instruments”; “Wethersfield, Connecticut” under “Birthplace”; and “Cambridge, 18 | Chapter 2: Creating and Managing Content Massachusetts” under “Lives In.” After clicking “Publish Now” return to the page at the permalink we used earlier to view the first version of our Bio item. Remember that “Publish Now,” not “Save” will make a change visible. Figure 2-3. New fields on the Bio type definition To add the field for uploading a profile image, an extra step is required. Since a text field obviously isn’t appropriate for an image, we’ll instead need to find a field with the functionality for uploading and inserting an image into our content item. However, Orchard doesn’t provide this field type by default. Fortunately, the Orchard Gallery offers just such a field. Clicking back to “Modules” and then the “Gallery” tab, we’ll add the image field by first searching for “image field.” From the results, select the Image Field module by Sebastien Ros (of the Orchard team). If you find multiple versions, install the latest version (1.1.3 as of this writing). After installing and enabling this module, we can now use it in our bio template. Returning back to the “Content Type” tab in the “Content” menu, again edit the Bio type. Click “Add Field” and select the new “Image Field” option. Set the display name to “Headshot,” accept the default technical name, and click “Save.” Now when we click back on “Content” and then click to edit the existing Bio item, we’ll see the ad- dition of the new image field. Adding a new image simply involves clicking “Choose File,” selecting a file from your PC, and again publishing the page. Bio Items | 19 Orchard does include a “Media Picker” field. This field does support uploading new images to content items. However, it won’t automati- cally display the image on the published item. Figure 2-4. A bio page with a headshot Projections and Queries As we’ve seen, we haven’t actually made our first bio page accessible from the site via a link. To do so, we have a couple of options. The simplest case would be to create a standard page (via New→Page) and to add links or a summary section for each bio page that we create. While this approach certainly would work, it has a flaw. Each time we create a bio, we’d have to go update this page and add the new bio links and summary section. Fortunately—as you might have guessed—Orchard has a better approach. A Projection is a content type that will display the results of a query in either grid or list form. We won’t actually write a SQL query to get the data for our projections. 20 | Chapter 2: Creating and Managing Content Instead, we’ll use the dashboard to select a filter, sort criteria, and a layout to compose an Orchard query. The items that match the conditions of our filter will be displayed in the projection page that we create. Since our projection page will be uninteresting with only a single bio, let’s take a quick detour to create two more Bio items. Return to New→Bio and create an item for drum- mer Greg Cocca and guitarist Nino Silvia. The specific content for these items isn’t as important as having content. We’ll start by creating a new query. In the admin menu on the dashboard, click “Quer- ies.” On the “Manage Queries” page, click “Add new query.” Set the title to “All Bios” and save the query. When you’re redirected to the “Manage Queries” page, click “All Bios” and you’ll see three options for our query. Click “Add a new filter” to set the data source for our new query. Filters We have several filter options from which to choose. For example, we could select “Body Part Text” to show only Bio items that have certain content (which we’d specify). We could select content with a certain title value or creation date. For our purposes though, keeping things simple is sufficient. We’ll just include all items that are Bio types. Under the “Content” section (of the filter page, not admin menu) click “Content Types” and select “Bio” from the next screen and save the form. If you click “Preview” after saving, you’ll see a preview page with the single Bio we’ve created so far. Sort Criteria Next, on the “Edit Query” page, click “Add a new Sort Criteria.” If you clicked to preview, just click “Back” in your browser as there’s no navigation out of the preview page. We’ll sort our query by name, which in our case is found in the Title part. Under “Title Part Record” click “Title Part Title” and select ascending from the next screen and save. Layouts The Layout option includes options for rendering our Bio items in a grid or an HTML list. In either case, we have a great deal of flexibility for managing the layout of these items. In fact, we are able to create multiple layouts so that we can have different views for the same query. Since we won’t be displaying our bio list in more than one place, a single layout will be sufficient. Click “Add a new layout” and select “Grid.” The HTML List offers similar options, but the grid has a little more flexibility, so we’ll choose that. The description section is used Layouts | 21 to disambiguate our layout when we’re creating projection pages. We’re creating only the one layout, so we can leave that blank. The “Display Mode” allows us to choose from all item values or individual properties. Select “Content” to accept the default rendering. Leave the alignment as “Horizontal” and set the “Number of Columns” to 1 (we’ll just stack our Bio items). If we wanted to be able to use CSS or JavaScript with these items, we could specify an ID for the grid, along with CSS class names for the grid and rows. We won’t be doing anything with the grid other than rendering it as is, so we can leave those fields blank for now. Save the layout. Now that we have a query, we are able to create our actual projection page. Click New→Projection from the dashboard menu. Add a title of “Bios” and let the Permalink default to the title. Under “For Query,” select the new query we just created—“All Bios (1 columns grid).” Leave the other defaults in place, except for “Show on main menu.” Check that option, and specify the “Menu Text” of “Bios.” We want “Bios” to appear alongside “Home” and “About.” If you save the projection and return to the live site, you’ll see the new “Bios” option on the menu. Click on it to see the queried item. Event Items Adding events to the Daisy’s Gone site will require a process similar to adding bio pages. Event will be a new content type that will be contained in a page that has a list of events (by way of a query and projection). Content Types and Fields Continued Let’s start by navigating to Content→Content Types and selecting “Create new type.” Name the new type “Event,” and like our Bio type, select the Autoroute, Body, Contain able, Menu, Publish Later, and Title parts. Also include the Comments part to allow users to add comments to the events. Click “Save” on the confirmation after saving the parts. Events will also need locations and dates. For location, add a text field named “Loca- tion” to the fields on the Event content type (click “Add Field”). For the event date, we’ll add a new date time field with a display name of “Event Date.” Accept the default technical name of “EventDate.” Click “Save” to make our new content type available with the fields we just added. When clicking New→Event, we can now see that the location and event date fields are present on our new content type. Create an event titled “Unplugged in Cambridge.” Recall that this title will be used as part of the URL that will be used to navigate to this page. This is the default behavior of the Autoroute content part. 22 | Chapter 2: Creating and Managing Content To see the other possible URL routing patterns we could select, click Content→Content Types and then click “Edit” on the Event type listing. Click on the greater than sign (>) next to the Autoroute part. Notice the “Default” pattern in the list. Its value is “{Con- tent.Slug}.” This pattern will simply create a slug (URL pattern) from the title you enter (basically, spaces are replaced by dashes). We’re not going to deviate from this pattern in this book, but you should click on the box in the “Pattern” field to see some of the other options you could use to create a route for custom content types. Also note that the previously created bio pages that we navigated to by adding names to the root site URL also worked via Autoroute (i.e., http: //localhost/orchard/john-zablocki). Figure 2-5. The Bio projection page Event Items | 23 Since we just abandoned the content type that we were creating, return to New→Event. Again, add the title “Unplugged in Cambridge.” Next add a brief description and the location “Cambridge, Massachusetts.” Also select a date for the event and check to allow comments to be added (this should be checked by default). Publishing the event makes it available to the site, but we still need to create a projection page before it will be visible via a menu item. Projections and Queries Continued Before we can create a new projection page for our events listing, we’re going to need to create a query (as we did for our Bio projection). Click “Queries,” then click “Add new query.” Enter the title “All Events” and save the query. Click into the query from the list that shows after saving. We’ll start this query simply by adding a new filter that contains all events. On the “Add a Filter” screen, click “Content Types” and select “Event” from the list that appears. After saving, click “Preview” to see that the query does in fact return our single Event item. We’re going to want to limit the events to only those that are in the future. Click again to add a new filter, but this time select “Event Date:Value” under “Event Content Fields.” We want only events that are occurring today or in the future, so select “Is greater than” in the “Operator” list. Since our source date will change daily, select “An offset from the current time” and enter “-1” for a value and “Day” for a unit. This combination will limit the query to events that are greater than yesterday. Since we’re going to want to see the next event at the top of the list, add sort criteria to sort on the “Event Date” field ascending. For the layout, add a simple one-column grid as we did when we created the layout for our Bio items. See the “All Bios” query earlier in this chapter for details. After adding the layout, save the query. We haven’t used the “Properties” display mode for our queries. This mode is far more powerful than the “Content” mode we’ve been using. However, “Content” is sufficient for our needs. Though we won’t cover “Properties” mode in any detail, it certainly merits a brief discussion as to when it would be useful. We would use “Properties” mode if we wanted to be able to customize everything from date formatting (for date time fields) to wrap- ping HTML elements around content item property values. You can also specify func- tionality, such as what to do when there are no results for a field and how to handle HTML tags and whitespace for a value. Most of the options you can choose from are straightforward. Now that we’ve created the “All Events” query, we’re able to create a new projection page that will display a list of our Event pages. Navigate to New→Projection. Enter the title “Events” and leave the permalink field blank to use the default behavior, which again will use the title. 24 | Chapter 2: Creating and Managing Content Select the “All Events (1 columns grid)” query and check the “Show on main menu” option. Set the “Menu Title” to “Events.” Otherwise, leave the defaults in place. Save the projection and return to the live site to see the Event listing on our new projection page. Click the event title to see the details with user comments enabled (Figure 2-6). Figure 2-6. An event page with commenting Daisy’s Blog Perhaps one of the most common use cases for a CMS such as Orchard is to create a blog. Blogging clearly helped WordPress to become what is arguably the most popular CMS on the market. For blog support, Orchard includes the Orchard.Blogs module, which provides standard blogging functionality, such as creating and maintaining posts and allowing authenticated comments. Many blogs are built as standalone websites. Like the site we’re building, these blogs have About and Contact pages. They also often include widgets for tag clouds and blog Daisy’s Blog | 25 rolls (links to other blogs). Creating such a standalone blog in Orchard is as simple as creating a new Orchard site and selecting the “Blog” recipe from the “Get Started” page. We’re instead going to include a blog as another section of our site. The Blog Module Click “Blog” to get to the “New Blog” form. Enter the title “Daisy’s Blog” and set the permalink to “blog.” If we had plans to create another blog for our site, we would leave the default permalink behavior of using the Title part’s value. Check the “Show on main menu” option and provide the “Menu text” value of “Blog.” Save the form and revisit the site to see the new “Blog” tab has been added to the main navigation. After saving, you’ll also notice that the “Blog” admin menu has been up- dated to include new options. “Manage Blog” is where you’ll edit existing blog posts and find the link to update a blog’s properties. “New Post” and “New Blog” should be self-explanatory. Before we create our first post, we’re going to update the permalink defaults used when blog posts are created. As was the case with the other content types we’ve seen, the default is simply to create a slug from the Title part of our post. We’re going to set our blog to use a traditional blog permalink format, which includes the date as pieces of the path. An example of such a URL is http://daisysgone.com/blog/2012/4/1/welcome-to -daisys-blog. Navigate to Content→Content Type and click “Edit” in the row for the Blog Post type. Click the greater than symbol (“>”) next to “Autoroute.” Change the “Name” field from “Blog and Title” to “Date and Title”; change the pattern to “{Content.Con- tainer}/{Content.Date.Format:yyyy/MM/dd}/{Content.Slug}”; and change the de- scription to “published-date/my-post.” The value “Content.Container” in this pattern will be replaced with “blog,” which was the permalink value we set for this blog. It’s important to understand that the Autoroute part won’t recreate the permalink as you update your posts (or any other content types that use the Autoroute part). If you want that behavior, then you need to check the option to “Automatically regenerate when editing content,” which appears right above the “Patterns” section of the Auto route part settings for a particular content type. Keep in mind that if you update a permalink that has been previously published, incoming links at the old URL will no longer be valid. After saving these changes, click “New Post” and in the form that follows, enter the title “Welcome to Daisy’s Blog!” Since we just went through the effort of creating a new pattern for our permalink, we’ll obviously leave that new default in place. Enter some content in the “Body” field. We’ll leave the “Tags” field blank and allow 26 | Chapter 2: Creating and Managing Content comments. Click “Publish Now” and then click the “Blog” tab on the site to see that this post is now listed (Figure 2-7). You can also see that its URL uses the pattern for its permalink. Figure 2-7. A blog post About Page The About page is pretty straightforward and doesn’t necessarily require any special Orchard functionality or custom content types. However, we’ll take this opportunity to upgrade the HTML and text editor that comes standard with Orchard, which is the TinyMce editor. TinyMce is probably sufficient for a most users, but I prefer CKEditor, which has richer editing capabilities. In the module gallery (Modules→Gallery) search for “editor” to find the “CKEditor” module, which we’ll install to add this popular web-based HTML and text editor. After enabling the CKEditor module, it’s necessary to disable the standard HTML and text editor, which takes precedence. Disable the default editor by clicking Mod- ules→Features, navigating to the “Input Editor” section, and disabling the TinyMce ed- itor. If you don’t disable the default editor, you’ll still be able to edit, just not with our updated HTML editor. After disabling TinyMce, we can click New→Page to see that the CKEditor is now the HTML and text editor for all content items that have a Body content part. Clicking Content→Content Items and then clicking on “About Daisy’s Gone” also shows the new CKEditor is used for existing content items (Figure 2-8). About Page | 27 Contact Page The easiest way to add a contact page to an Orchard site is to install one of the contact modules from the Orchard gallery. A search for “contact” in the gallery will yield a few different options. We’ll install “Contact Form” by CyberStride. After enabling this module, a new content type of Contact Page is automatically added to our site. Clicking New→Contact Page will open a form for creating Contact Page items. One field that is noticeably absent from this form is the ability to add a contact page to the site’s main menu. Fortunately, as we saw when creating the Bio and Event content types, this functionality is easy to add. By clicking Content→Content Type→Edit, we can add the content part. Click “Add Parts” and check off the Autoroute, Menu, and Title parts and click “Save.” Return to New→Contact Page. Creating the contact page involves inputting some straightforward values for title and body. The “Email notification to” field needs to be set to the username of a valid user in the system. After saving the form and refreshing the site, we can click over to the “Contact” tab on the main navigation to see the new page (Figure 2-10). The contact form includes fields for collecting name, email, and a message, all of which are required. If we fill out the form and click Submit, our message is sent. Back on the Figure 2-8. The About page with the CKEditor 28 | Chapter 2: Creating and Managing Content admin pages, we can browse to “Contact Requests” and see a listing of attempted contact form submissions, both successful and failed (validation failures). For an email to be sent when a contact request is made, we need to enable the Email Messaging module in the “Modules” admin page. When we enable the Email module, Orchard will display a yellow warning message that we need to configure SMTP set- tings. To set these, we can either click the link in the warning or navigate to Set- tings→Email. You’ll need a valid SMTP server through which you can relay messages in order to send email in Orchard. If you get an error while clicking the “Contact Requests” page in the admin dashboard, you’ll need to edit the view file index.cshtml in the Admin views directory of the module. At the time of this writing, this page uses the DateTime HtmlHelper extension method, which is no longer present in the current version of Orchard. Simply remove the method call and set the line to @contact.ContactDateUtc.GetVa lueOrDefault(). Figure 2-9. The new contact page form Contact Page | 29 Home Page For the home page, we’ll start by making a couple of minor content changes. Under “Content” we’ll click on the listing currently titled “Welcome to Orchard!” and update its title to “Daisy’s Online.” Clicking “Publish Now” makes these changes available on the live site. Note that because this page has been set to the home page by way of the “Set as home page” field, we can get to this page at the root site URL http://localhost/ orchard. Figure 2-10. The Daisy’s Gone contact page 30 | Chapter 2: Creating and Managing Content Zones and Layers At this point, our home page hasn’t been customized much beyond the small tweaks we made in the previous chapter. To add additional customization, we’re now going to add a couple of new modules from the Gallery that we’ll then add to the TripelSecond zone. We want to give visitors the ability to “Like” the band on Facebook right from the home page. Searching for “Facebook” in the Gallery yields more than a handful of results. We’re going to install the Facebook.Like module by David Fekke. After installing and enabling this module, we’ll add it to the home page. We’ll also add a Twitter follow button by installing the Twitter Follow Button module by Nicholas Mayne (search for “twitter”). To add the Facebook widget to the home page, click over to “Widgets” and set the current layer to “TheHomepage.” Remove the “Second Leader Aside” widget from the TripelSecond zone. Then click “Add” on “TripelSecond” and select the Facebook Like widget. In the form for this widget, enter the title “Like Daisy’s Gone on Face- book.” Leave the other defaults and save. Repeat these steps to add the Twitter Follow widget also to the TripelSecond zone. Use the title “Follow” and provide the Twitter username “@daisysgone”. After saving both widgets, refresh the home page to see that both widgets have been added. Figure 2-11. Social widgets on the home page Zones and Layers | 31 Clicking to other pages on the site, you’ll notice that these widgets don’t appear any- where but the home page. The reason for this behavior is that the rule that determines whether to display these widgets is defined in the layer “TheHomepage.” Clicking on “Widgets,” selecting the layer “TheHomepage,” and clicking “Edit” displays a form for editing the layer. The “Layer Rule” field defines a Boolean expression that determines whether a widget is displayed. “TheHomepage” has a rule url '~/’ that is true when the URL is the root site URL. Layer rules have a terse syntax, using the logical operators not, and, and or along with the url and authenticated functions. To demonstrate how to use layers, we’re going to add a new Projection widget to the zone AsideSecond on all pages except the events page. On the Widgets admin page, you probably noticed the image that shows where zones are placed in the layout. Note that this image was created by the theme designer and will only be accurate if the CSS rules render zones in a way that matches the zones as shown in the image. If you modify the theme’s CSS rules, there’s no guarantee this image will re- main accurate. Click Widgets→Add a new layer. Name this layer “NotTheEventsPage”. Add a rule not url '~/events’ and then save. After saving, Orchard returns to the “Widgets” admin page with the new layer selected. Click “Add” on AsideSecond and select “Projection Widget.” Enter a title of “Upcoming Events.” Select the query “All Events (1 columns grid)”, and limit the number of items to display to 3. After saving and refreshing the site, we can see that the new events projection widget appears on all pages except for the events page. Gallery For the gallery page, we’re going to install and use the Image Gallery module by Gabriel Eduardo Chites de Mello. After installing and enabling this module, a new admin menu option is added. Click Image Gallery→Add new Image Gallery. Name the new gallery “Gallery Page.” After saving, click the new gallery’s name in the listing and then upload new images by clicking “Add Images” and using the upload form. Again, click Widgets→Add New Layer... and name that layer “Gallery.” Set the rule to url '~/gallery’ so that we can add the Image Gallery widget to a page at this URL only. After saving the layer add the Image Gallery widget to the AfterContent zone. Leave the defaults, other than the required title. 32 | Chapter 2: Creating and Managing Content We’ll next create the actual gallery page via New→Page. Add some content, add the title “Daisy’s Gallery,” set the permalink to “gallery” to match our rule, add the page to the main menu with the text “Gallery,” and publish. After publishing, you can browse to this page by clicking the “Gallery” tab on the main menu (Figure 2-13). I originally planned to name the “Gallery” page “Media.” However, Media is already a special directory in Orchard and the URL route col- lided resulting in a 404 Not Found error by IIS. Figure 2-12. The new NotTheEvents layer Gallery | 33 Figure 2-13. The Image Gallery Summary At this point, we’ve touched upon most of the core content management features of Orchard. We’ve seen how easy it can be to create new content types. We’ve also seen how we can augment existing types. In the next chapter, we’re going to look at cus- tomizing how the content we create is displayed by Orchard. 34 | Chapter 2: Creating and Managing Content CHAPTER 3 Displaying Content As we saw in the previous chapter, getting a basic Orchard site up and running is a pretty low friction process. Without having to write a single line of code, we were able to create new pages with dynamic content and reasonably advanced functionality. However, we also saw that the content we displayed has a default rendering, which we can’t fully customize through the admin tools. Fortunately, Orchard does provide functionality to allow us to alter this rendering. In fact, with minimal effort Orchard gives us great control over how we display content. Virtually every piece of data that gets rendered by Orchard (collectively known as shapes) may be customized by overriding its default template. In this chapter, we’ll learn about the conventions and tools that make this process relatively straightforward. Customizing Biography Content As it exists now, the bio projection page simply takes each field in our Bio content type and renders its label and value one after the other with each on its own line (see Fig- ure 3-1). It would be better if our content read a little more naturally, such as “John Zablocki was born in Wethersfield, Connecticut, and plays guitar for Daisy’s Gone.” The label-value rendering might be sufficient for a product listing, but is less suited for our biography listing. Ultimately, we’ll create a template for rendering Bio content items in a projection page (or any other container). However, to demonstrate how Orchard alternates work, we’ll start at a higher level and change the way all content is displayed when it’s rendered in a container (a projection in our case). Orchard content is rendered differently based on its usage. For example, when content items such as those created from our Bio type are added to a projection page, their summary display type is used. When a full content item is rendered (i.e., an individual biography), then the detail display type is used. 35 Content Templates If you open up the Themes project in your Orchard solution (in the “Themes” solution folder) you’ll find a directory named TheThemeMachine, which is the default Orchard theme. The styles associated with this theme are kept intentionally simple, because this theme is generally used as a starting point for building new themes. In the next chapter, we’ll create a new theme from scratch, but for now we’re going to work within this one. The first thing to do is expand the Views directory. There you’ll see six Razor (.cshtml) files already in this folder. These files are all used by the “TheThemeMachine” theme. We won’t explore these files now as we’ll discuss them in the next chapter. Add a file to this directory, named Content.Summary.cshtml. If you clear the contents of this new Razor file, save it and then refresh the “Bios” projection page, what you’ll see (Figure 3-2) is that the Bio item summaries have disappeared. Also notice that the “Upcoming Events” are no longer appearing in the projection widget that we added to the AsideSecond zone. Figure 3-1. The default bio projection page 36 | Chapter 3: Displaying Content The Themes project was not created using an ASP.NET MVC Visual Studio project template, therefore the usual MVC context menu short- cuts are absent from this project (i.e., “Add View”). Instead, I use Add→New Item and select “HTML Page” from the list of web templates. This action requires manually setting the .cshtml extension. As you might suspect, Orchard found our new alternate template and used that file to render the summary display for our Bio items. You might also have realized that like our “Upcoming Events,” the Event and Blog Post items have also disappeared. You can verify this by clicking on the “Events” or “Blog” tabs. What this change is starting to demonstrate is how Orchard performs the task of “shape rendering.” Figure 3-2. The effects of a blank Content.Summary.cshtml file Alternate Templates There are several file and directory naming conventions used by Orchard to determine which template should be used to render a particular piece of content. In our example, creating a view named Content.Summary instructed Orchard to use this template for all content shapes rendered with the summary display type. These views are known as “alternate templates.” Again, content items in a projection page are rendered using the “summary” display type. We could edit our alternate template to add the header (title) and content (ev- erything else) back in. To do so, we need to add only two lines of code. For illustrative purposes only, we’ll also change the background color of the content:
@Display(Model.Header) @Display(Model.Content)
If you save the template and refresh the bio, event, and blog projection pages, you’ll see that the content is again being rendered as before, but now with a gray background. You’ll also see our “Upcoming Events” widget being rendered the same way. Customizing Biography Content | 37 To change this alternate template so that only Bio content items are affected by the customization, you simply have to rename the file to Content-Bio.Summary.cshtml. Orchard will parse the token after the hyphen and match it to a content type id. In this case “Bio” was the default content type id associated with the Bio content type when we created it. With this change, Event and Blog Post items are no longer rendered with a gray background within their respective containers. You could also rename the file to Content-17.Summary.cshtml, where “17” is the unique identifier of a particular piece of content (my bio in my case). After renaming the file, if you refresh the “Bios” page you’ll see that only the bio summary specified in the filename has been grayed (Figure 3-3). This item level customization might be useful for calling out a particular item in a list, such as a special event. Figure 3-3. A template for a single content item Let’s rename our alternate template back to Content-Bio.Summary.cshtml so that we can return to our original goal, which was to render our bio listings with more natural language. To achieve this goal, we’re going to create a simple template that will display values from our content type in an order that we define. 38 | Chapter 3: Displaying Content Our template first defines a simple Razor function splitName for convenience. We’re going to take the shortcut of parsing our first name out of the title, as opposed to creating a separate field for this value in our type definition. We’ll then check whether a profile picture has been set. If it has, we’ll display it: @{ Func splitName = (name) => name.Split(' '); }
@{ var fileName = Model.ContentItem.Bio.Headshot.FileName; if (! string.IsNullOrEmpty(@fileName)) { @Model.Title } } @Model.ContentItem.TitlePart.Title plays @Model.ContentItem.Bio.Instruments.Value.ToLower() for Daisy's Gone.

@splitName(Model.ContentItem.TitlePart.Title)[0] is originally from @Model.ContentItem.Bio.Birthplace.Value and currently lives in @Model.ContentItem.Bio.LivesIn.Value.

Next we display a link to the full bio page. We set the href property of the anchor tag by accessing the Bio content item’s Autoroute content part, which has the URL slug for permalinks. We then construct a sentence that summarizes a little information about each musician. In this sentence, we’ll access both the Bio type’s fields and its Title part to fill in the template values. The important thing to note is that if you want to reference fields from a content item in your templates, you’ll need to use a dynamic expression that is generally of the form Model.ContentItem.{ContentType}.{FieldName}.Value. Other elements such as the title require first accessing its content part. Later on in this chapter, we’ll see how to discover these values using the Orchard tools. We just fixed our bio summary templates so that the content wouldn’t just dump out labels and values for each property. However, when we click through to an individual bio, we can see that the detail view for our page looks like our summary did before our customization. To get our bio details and summary views more in line, we’ll add a new alternate tem- plate. Start by creating a new Razor file named Content-Bio.cshtml in the Views directory in “TheThemeMachine.” This new template will be only a slight variation of the Bio item summary template we just created:


@{ var fileName = Model.ContentItem.Bio.Headshot.FileName; if (!string.IsNullOrEmpty(fileName)) { Customizing Biography Content | 39 @Model.Title } } Plays @Model.ContentItem.Bio.Instruments.Value.ToLower() for Daisy's Gone.

Originally from @Display(Model.ContentItem.Bio.Birthplace.Value).

Currently lives in @Model.ContentItem.Bio.LivesIn.Value.

@Html.Raw(Model.ContentItem.BodyPart.Text) Figure 3-4. The customized bio listing It’s also worth noting that if we removed the summary alternate template, this new template would then be used to render summary listings as well since the name of this file doesn’t specify a display type (i.e., Summary or Detail). We could also have (and probably should have) named this file Content-Bio.Detail.cshtml to remove the poten- tial impact on summary displays. Since shapes are dynamic types, it’s occasionally helpful to attach the Visual Studio debugger to your Orchard application to inspect the ob- ject being bound to your view. You can set a break point in a Razor file in a code block, which is any executable line between a @{ }. 40 | Chapter 3: Displaying Content Customizing Events Like our bios in our bio listing, events in our events listing are rendered one property at a time with labels and values occupying one line each. Arguably, this display is ap- propriate for events, so we won’t create an alternate template for event summaries. However, we do want to adjust the order in which fields are rendering. Figure 3-5 shows that the location appears before the body field, while the event date is after. We’ll move both of these fields ahead of the HTML body field. Figure 3-5. Default event listing Placement Files To rearrange the fields, we’ll use a special XML file named Placement.info that lives in the root of a template directory. This file defines rules for rendering content items, parts, and fields. “Place rules” allow module developers and template designers to pro- vide preferred layout ordering for content pieces. We’ll modify the Placement.info file in the root directory of the “TheThemeMachine” theme by replacing its content entirely with this XML: Customizing Biography Content | 41 The Match element will limit the placement rule so that it affects only items of type Event that are shown with the summary display type. The Place rules that follow the Match specify that the text and date time fields will appear before the Content zone, where the HTML body is placed. If we want to force the event date to render after the location, we could provide relative numeric values instead of “before” or “after”: Field Templates We also want to jazz up our location field so that it renders a link to Bing Maps for that particular location. To do so, we’ll create an alternate template for just that field. This is different from what we did with Bio content, where the alternate template affected an entire item’s rendering. Create a new directory named Fields under the Views directory in the “TheThemeMa- chine” theme. To that new directory, add a file named Common.Text-Event-Loca- tion.cshtml (prefixing this file with Fields would make the new directory unnecessary). The body of this new template will simply supply the value of our event’s location to the “q” parameter used by Bing Maps when it performs a search: @Model.Name: @Model.Value

If you refresh the event listing, you’ll now see the location field is wrapped in a link to Bing Maps. If you click through to view the actual event, you’ll see that the location field is also linking to Bing Maps (Figure 3-6). Our field alternate applies to both sum- mary and detail displays. There is still one last problem we want to fix. The ordering of the event fields in the details display type is not the way we want it. The event date is rendering below the content as was previously the case with event summary displays (Figure 3-6). To fix this, we’ll simply remove the Match tag from Placement.info where we’d previously re- stricted the display rule to summary only: 42 | Chapter 3: Displaying Content Shape Tracing While for some shapes it’s easy enough to figure out the correct naming for alternate templates based on the documented rules, it’s not always obvious. Fortunately, there’s the Shape Tracing module to help navigate the complex hierarchy of shapes that com- pose a page. Shape Tracing is installed but disabled when you create a site with the “Default” recipe. It can be enabled on the admin dashboard under Modules→Features. It’s listed under the “Designer” category. After you enable the Shape Tracing module, across the bottom of each page of your site you’ll see a small bar with a small square icon all the way to the right. Clicking it will bring up the tools for outlining the shapes on a page. Shape Tracing is a JavaScript-based utility that provides information about how zones and the shapes contained in these zones are rendered. As you move your mouse around the page, the Shape Tracing tool highlights content pieces such as zones, widgets, con- tent parts, and fields. Figure 3-6. The event location linking to Bing Maps Customizing Biography Content | 43 Figure 3-7. The Shape Tracing developer tools Finding alternate templates To demonstrate the utility of the Shape Tracing module, we’ll create an alternate tem- plate for the content of the “Body” field on our home page. We’re going to add an image to the content and float it to the left of the text. We could use the HTML editor to add an image, but this template will give us a little more control over layout. With the Shape Tracing module activated (click the little square), mouse-over the Content zone. Click when one of the paragraphs is highlighted. The shape tracing con- sole will display information under the “Shape” panel (Figure 3-7). If you expand the “Alternate” tree under the “Shape” panel, you’ll see three possible options for creating an alternate template. Next to each option is a “Create” button that you can click to have the tool generate an alternate template for you in your current theme. Click “Create” to generate the Parts.Common.Body-11.cshtml. Recall that the number in the filename is the unique content ID of a content item and yours will vary. In order to navigate your site normally again, you must click the dash icon in the upper-right corner of the Shape Tracing tool, to turn off its functionality. 44 | Chapter 3: Displaying Content If you use the Shape Tracing tool to create new alternate templates, these files won’t be added to the project automatically. You’ll need to click “Show All Files” in Visual Studio’s Solution Explorer to see them. You can then select “Include in Project” from the context menu. After you click to create this new alternate template, back in the Views directory of the “TheThemeMachine” theme, you’ll find the new file. By default its code just renders the HTML property of the Model property of the page. The Shape Tracing module creates alternate templates based on current template for that piece of content. Now we’ll add the new image for our home page. Create a new media folder by clicking Media→Add a Folder. Name the new folder “Art” and click “Save.” Click into “Art” and then click “Add Media” and upload an image. After it’s been uploaded, click the filename to get its path (copy the “Embed” value). Then back in Visual Studio, open Parts.Common.Body-11.cshtml and add an HTML img tag with the source set to this new image. We’ll render the saved HTML (which is simply the “Body” content) next to our Daisy’s Gone logo: @Model.Html Figure 3-8. The Shape Tracing tool after clicking on a shape Customizing Biography Content | 45 We also could have named the template Parts.Common.Body-Page.cshtml. This naming would have resulted in all pages with a Body part being affected. For our site, that would have meant the home, contact, gallery, and about pages would all include the new image. Even the “News and Notes” HTML widget would be affected. Figure 3-9. The alternate Body template Finding shape properties The Shape Tracing module also provides us with a way to figure out object hierarchies for our template alternates. If you wondered how the expression Model.ContentI tem.AutoroutePart.DisplayAlias was discovered for use in our bio templates, it was the Shape Tracing module. Hover again over a piece of content while the Shape Tracing module is enabled, but this time click the “Model” tab in the console. You’ll see a tree (Figure 3-10) that con- tains information about the ContentItem property of the view’s Model property. You’ll see details of content parts and fields. As you click through the tree, the expres- sion to access these properties appears just below the tabs. Simply copy that expression 46 | Chapter 3: Displaying Content into your alternate template. For example, you could highlight an event in our “Up- coming Events” widget and click through the model to find the location expression @Model.ContentItem.Event.Location. The Shape Tracing module can also be used to see the placement rules for a piece of content, the current template used to display that content and the actual HTML ren- dered for that content. It’s an incredibly powerful tool to have at your disposal. You’ll use it frequently while customizing the display of content on your site. Just don’t forget to disable the module if you’ve enabled it on your live site for testing. Figure 3-10. The Shape Tracing model panel Summary We’ve seen in this chapter that Orchard provides extensive ways to customize content. We can provide alternate renderings for full content items or individual fields within our content. Our alternates were somewhat utilitarian in that they offered improved and new functionality or changed the way something rendered. The second part of customizing content in Orchard is to create striking visual changes to the way content is rendered. We’ll learn how to make such changes in Chapter 4. Summary | 47 CHAPTER 4 Creating Themes In the previous chapter, we saw that it’s possible to customize the look and feel of an Orchard site by creating alternate templates for pieces of content. While this feature does provide some flexibility over how content is rendered on pages, it doesn’t easily allow for wholesale changes to the way a site looks. In order to achieve this broader goal, we’ll look at creating our own themes. We’ve already taken a brief tour of the default theme—“TheThemeMachine”—that is part of a standard Orchard installation. In this chapter, we’re going to take a look inside of that theme to understand how to create our own. At this point, our Orchard development has been limited to editing a couple of view files. We wrote some C# code in those Razor templates and learned a little about how the content is modeled and displayed in a view. However, we haven’t really been ex- posed to the Orchard development experience. To gain this exposure, we first need to learn about a couple of tools that make Orchard development easier. The Orchard Command-Line Interface It probably seems strange to consider using a command-line interface (CLI) with a web- based CMS. However, the Orchard CLI offers quick access to many common admin functions without the need to open up a browser and navigate to different property pages. Assuming you’ve been working in the Orchard solution, the CLI has been ready for use since you first compiled your app and set up your recipe. Getting Help To get started with Orchard’s CLI, open up a command window (or PowerShell) and navigate to the bin directory of your Orchard site. There you’ll find file Orchard.exe. Execute that file. After a few moments, you’ll see an Orchard prompt. PS C:\dev\Orchard> cd .\src\Orchard.Web\bin PS C:\dev\Orchard\src\Orchard.Web\bin> .\Orchard.exe 49 Intializing Orchard Session. (This might take a few seconds...) Type “?” for help, “exit” to exit, cls to clear screen orchard> There are a number of commands you could execute. To get CLI help, there are two commands you should know. The first simply tells you how to perform tasks like quit- ting, clearing the screen, and getting more help: orchard> help One of the items listed when you execute the help command explains how to get help for the commands that allow you to work with your Orchard site: orchard> help commands Executing help commands gives you a list of CLI commands ranging from user creation to page creation. You’ll also see how to go one level deeper for command help: orchard> help page create To test your session, enter the command that simply lists the site cultures, which are used for the internationalization of your site: orchard> cultures list Listing Cultures: en-US Code Generation Tools If you ran the help commands, you probably saw that the Orchard CLI has commands for many of the common tasks you’d normally perform in the admin pages, including enabling and disabling features. We’re going to use this command to enable a feature that will give us additional command line tools: orchard> feature enable Orchard.CodeGeneration Enabling features Orchard.CodeGeneration Code Generation was enabled Once the code generation tools are enabled, they will provide useful shortcuts for cre- ating and managing themes and modules. Orchard.CodeGeneration is an example of the Orchard’s extensibility. In fact, you can build your own command-line tools for Orchard. It’s as simple as creating a class that extends the base class DefaultOrchard CommandHandler. The code generation module is not installed by default with Orchard, but it was found in the source when you downloaded the zip or cloned the repository. If you are not working with the solution, you might need to install this module from the Orchard Gallery. 50 | Chapter 4: Creating Themes Code Generating a Theme In the previous chapter, we saw that a theme is a collection of files contained in a directory in the Themes project in the Orchard solution. There’s nothing special about a theme other than it follows a set of conventions and is stored in the Themes directory under the Orchard.Web project. To start building a new theme, you could simply copy the directory structure for the “TheThemeMachine” theme and paste it into a new sibling directory. We’ll instead use the command line code generation options to create our theme. Later we’ll learn how these tools can help us avoid starting from scratch. The Orchard solution is organized using solution folders, so it appears that the Themes project lives outside of the web project’s directory struc- ture. However, the Themes project and theme files are actually nested in the filesystem under the Orchard.Web directory. Return to the CLI and your Orchard prompt. We’re going to create a new theme named “DaisysTheme.” There are three options we’ll want to consider before we create our new theme: CreateProject Whether to create new project for this theme. The default is false. IncludeInSolution Include this theme in the solution. The default is true. BasedOn Inherit default templates from an existing theme. For our new theme, we’re neither going to inherit from an existing theme nor create a new project. We’ll simply run the theme code generation without any arguments: orchard> codegen theme DaisysTheme Creating Theme DaisysTheme Theme DaisysTheme created successfully Return to Visual Studio where you’ll be prompted to reload the solution. The codegen utility modified the Themes project and forced a reload of the solution. After reloading, we can start to inspect the anatomy of our new theme. Code Generation Tools | 51 The Structure of a Theme The structure might look somewhat familiar to an experienced ASP.NET MVC devel- oper. As is the case with the standard Visual Studio ASP.NET MVC project template, there are directories for Scripts, Styles, and Views. The purpose of each of these and the other directories is as follows: Scripts Directory for JavaScript files Styles Directory for CSS files Views Directory for Razor template (*.cshtml) files Content Directory for images and other static content Zones Directory for templates that wrap zones Additionally, the generated theme template includes a Placement.info file. Recall that this is the XML file that instructs Orchard as to how or whether to layout fields, parts, and items. There are also numerous Web.config files that are used by ASP.NET to set up some configuration plumbing for ASP.NET and ASP.NET MVC. There are two other files worth noting, namely Theme.txt and Theme.png. Both of these files are used by Orchard to describe your theme to the admin pages. Back in the admin dashboard, select Themes→Installed. You’ll see three themes listed (Figure 4-1). The current theme will be the default “TheThemeMachine.” There’s a second theme called “The Journalist” and our new theme named “DaisysTheme.” However, some things don’t look quite right with our new theme. Notice that the name is “DaisysTheme” without a space. Authorship is attributed to “The Orchard Team.” The version is already set to 1.0 and the description and URL are also wrong. As we’ll see shortly, the preview image also fails to accurately represent our new theme at this stage. You might have guessed that Orchard is using Theme.txt and Theme.png to determine what values to plug into this admin page. Before returning to Visual Studio to look at these files, let’s first make our new template the current template by clicking “Set Current.” Once the switch is complete, refresh your site. What you’ll see is that your site is suddenly without any discernible structure or styling (Figure 4-2). Also notice that our alternates are gone. We’ll add some of those pieces before we modify the metadata that’s used by the Dashboard. 52 | Chapter 4: Creating Themes The purpose of this chapter is to introduce theme development. As I am not a designer, I am intentionally keeping the theme we will build sim- ple, including all graphical treatments, styles, and HTML. Default Content Templates If you view the source for the home page, you’ll notice that there is a full HTML docu- ment wrapping the content. This might seem strange, since we haven’t actually defined any master page or layout files. These default template files do exist, though. Orchard includes them in the Orchard.Core project. Figure 4-1. Installed themes The Structure of a Theme | 53 If you expand that project in Visual Studio’s Solution Explorer, you’ll see a Views directory nested under a Shapes directory. The view files inside this directory are used by Orchard as safe defaults for displaying content when no suitable template alternates are found in a theme for a given piece of content. Open the files Document.cshtml and Layout.cshtml to see the HTML that is wrapping the content in our home page. Document.cshtml defines the basic structure of an HTML document, including the html, head, title, and body tags. Layout.cshtml defines a very basic arrangement of div elements on the page. Notice that the template for the title of the page is actually found in the Parts.Title.cshtml file in the Views directory under Title in the Orchard.Core project. As is hopefully now clear, Orchard uses a hierarchy of templates when determining how to render content. In the previous chapter, when we defined alternate templates, we simply added files to the current theme and those files took precedence over those found in the default templates directories. When creating a theme or module (in the next chapter), you could choose to inherit a template or override it at any level (item, type, part, or field). Working with Views In Chapter 3, we created alternate templates in the form of Razor template files. How- ever, we didn’t explore these templates in any detail. Before we build a theme and continue our Razor efforts, it’s worth a quick look at some of the functionality that Orchard adds to the standard base class used by Razor views. For more on Razor, see Programming Razor by Jess Chadwick (O’Reilly). Figure 4-2. A new theme without any styles or structure 54 | Chapter 4: Creating Themes Orchard extends the System.Web.Mvc.WebViewPage base class used by Razor views with its own WebViewPage. We’ve already seen a method from this subclass. When we created our zones, we used its Display method. There are other useful helper methods in this base class. The Style property of WebViewPage has an Include method that will render a link tag that points to the filename provided as its argument. It assumes that your file is in the Styles directory of your theme. Similarly, there is a Script property with methods related to including JavaScript blocks and files. Both the Script and Style properties are of type ResourceRegister, which provides an additional method named Require. Given a resource defined in a manifest class (we’ll learn more about this class in Creating Widgets), Require will find a script or style and ensure that it’s included only once. WebViewPage also includes some convenience methods, such as the null and whitespace checking HasText method. Orchard also provides a StringExtensions class in the Orchard.Utilities.Extensions namespace. This class has methods such as Camel Friendly, Ellipsize, and HtmlClassify, all of which may be useful in views. Layouts For our theme, we’ll consider this document wrapper sufficient and won’t override it with a new Document.cshtml. We’ll instead start our theme with a new layout file. Add a new Razor file named Layout.cshtml to the Views folder in the “DaisysTheme” theme. If you save this file with no content (an empty HTML file) and refresh any page on your site, you’ll see that the content has disappeared. By including a Layout.cshtml file in our template, we’ve instructed Orchard not to use its default layout template. Instead we’ve instructed Orchard to use this new, empty file. Notice though that the page title still appears. As mentioned, the title was included in Document.cshtml, which we chose not to override. If you’ve viewed the source of any of your site’s pages as we’ve been making changes to the theme, you may have noticed a great deal of JavaScript content. The script is from the Shape Tracing module that we enabled in the previous chapter. It would not appear on production sites unless you left that module enabled. We’ll add some code to Layout.cshtml to get some content back on our site. Start by adding the following snippet:

@if (Model.Content != null) { @Display(Model.Content) }
Layouts | 55 This simple chunk of Razor code demonstrates a couple of key patterns for building layouts in Orchard. We’re going to explore this pattern in more detail later in this chapter. For now, recognize that we null-check a property on our view’s Model and call Display on that property when it’s not null. Recall that the data bound to our views are dynamic types known as shapes. The Model property of our view is a representation of these shapes. When you call the Display method, it’s going to check the runtime type of the argument you provided. In this case, the type will contain metadata indicating that it’s a “zone,” which will allow the Display method to properly render content in that given zone. The Display method is actually a read-only dynamic property that is de- fined in Orchard’s WebViewPage, which is the base page type for Razor views in Orchard. This property returns an instance of a callable dynamic object, which is why the method-call syntax works. Saving the layout and refreshing the home page, we now see that the main content has been added. However, we’ve lost our navigation and other zones. Let’s add the navi- gation by placing the block of code that follows above the content snippet we previously entered: @if (Model.Navigation != null) {
} Once navigation has been added, we can now refresh the home page and click through each of the pages to see that content is in fact displaying on each page. While most pages look like their “TheThemeMachine” equivalents without any CSS, the home page is noticeably missing the widgets we’d previously added. If we want the Bing Maps widget to show up in our new theme, we need to include a zone named TripelThird and a block of Razor/HTML as follows: @if (Model.TripelThird != null) {
} Zones and Layers At this point we can see that zones are simply sections defined in our layout templates. We use the Display method of the view to create them. Adding widgets to zones in the admin tool creates the relationship that allows the null-checks that surround the Dis play calls to evaluate properly. Had we not added a widget to the zone TripelThird, then Model.TripelThird would evaluate to null. 56 | Chapter 4: Creating Themes Let’s take a quick detour from Daisy’s Theme to explore zones a little deeper. Start by opening up Theme.txt in our theme’s root directory. The “Zones” entry in this file is used by Orchard to display the list of zones that appear when you click “Widgets” on the admin menu. That list is currently populated by zones defined in other installed themes and Orchard will tell you as much when you visit that page. Add a “Zones” section to your Theme.txt named “MoreContent”: Zones: MoreContent Next return to Layout.cshtml and add a new zone: @if (Model.MoreContent != null) {
} If you refresh the “Widgets” admin page, you’ll now see a zone named “MoreContent” above the zones defined in “TheThemeMachine.” Click Add→Html Widget, add some content, and save. Next, refresh the home page (or any other page). You’ll now see that the zone is displaying on each page (Figure 4-5). Let’s limit this new widget so that it appears only on the home page. Click on the “More Daisy’s Content” link (the name of the HTML widget) listed with the “MoreContent” zone. On the property page for that widget, choose the layer named “TheHomepage” and click Save. Click through to each of the pages to see that the layer rule has enabled this zone only for the home page. Alternate Templates As you navigate around the site, you’ll notice that we’ve lost the customization built in Chapter 3 for rendering bios and events. If we want to get these templates back, all we need to do is add those Razor files into our new template. If you move or copy Content-Bio.Summary.cshtml to the Views directory of our “Dai- sysTheme” theme and refresh the bio page, you’ll see the listed bios are displaying content as they were previously. Of course, without any CSS in our theme you’ll notice that the rendering lacks any style (Figure 4-4). Theme Inheritance At this point, our theme isn’t particularly stylish or interesting. Our layout is pretty limiting as well. What we really want is some HTML that’s easily styled by a skilled designer. Fortunately, the work for that has already been done. The theme “TheThemeMachine” defines a very flexible layout file. We could simply copy that into our theme, but instead we’re going to inherit it into our theme. In Alternate Templates | 57 “DaisysTheme” open Theme.txt and add the line that follows. Then delete Lay- out.cshtml (the one we created): BaseTheme: TheThemeMachine After you refresh the home page, you’ll see that our site has returned to its “TheThe- meMachine” roots. However, we obviously want to customize our look and feel a bit. To deviate from the inherited theme, we need to override the default styles found in “TheThemeMachine.” Create a new file Site.css in the Styles directory of the “DaisysTheme” theme. After you create the empty stylesheet, you’ll see after refreshing your site that we’ve again lost our styling, but maintained our layout and alternate templates (Figure 4-5). Figure 4-3. The MoreContent zone 58 | Chapter 4: Creating Themes Unfortunately, there’s no way for our theme to inherit both the layout and stylesheet from the “TheThemeMachine” theme. Layout.cshtml explicitly includes only a single stylesheet named Site.css. If we want to inherit the entire layout file and customize the style, we have to copy the contents of Site.css from the Styles directory of “TheTheme- Machine” into our new file. Otherwise, we have to modify the layout file to look for an additional stylesheet. Figure 4-4. An alternate template in the DaisysTheme theme Alternate Templates | 59 Basic Styling Copy the stylesheet content over into our new theme (Site.css) and save the stylesheet file. Refresh the site to see that we’re now back to the “TheThemeMachine.” Again, we’ll leave the design lessons for the designers, but we’ll modify some of the basic UI to make our theme a little more unique. Since we’re designing a site for a rock band, we’ll change the background color to a blackish color. Locate the body selector in the stylesheet and we’ll go from a light theme to dark simply by changing the background color: body { line-height: 1; font-size: 81.3%; color: #434343; background: #303030; font-family: Tahoma, "Helvetica Neue", Arial, Helvetica, sans-serif; } Figure 4-5. DaisysTheme inheriting layout from TheThemeMachine 60 | Chapter 4: Creating Themes Of course, it’s a bit hard to read the gray text on the dark-gray background. So let’s lighten up our content areas. We could go into the individual page sections and set each to have a white background, but there’s an easier way. We can take advantage of the fact that the layout from which we’re inheriting wraps various groups of zones in div elements with a class name of “group”: .group { background: #fff; } Let’s also update the font that’s used for the header of the site. By default it uses a font named “Lobster.” You’re probably thinking that you don’t have “Lobster” installed, yet you’ve somehow been seeing the correct cursive font on the header. Orchard as- sumes modern web standards by default, so our theme is able to make use of the @font- face directive in CSS3. More specifically, it uses the Google Web Fonts API: Style.Include("http://fonts.googleapis.com/css?family=Lobster"); If we want to change this font, we have a few options. We’re again faced with the dilemma of whether to modify or copy Layout.cshtml in “TheThemeMachine” to in- clude our desired change. Since we’re just changing styles, we’re going to keep the layout in place and take a different approach. We’ll simply import a new web font in our stylesheet and then set the branding ele- ment’s font-family to our new font. Start by adding a new @import directive to the top of our stylesheet: @import url(http://fonts.googleapis.com/css?family=Frijole); In our template, the header text is rendered in an h1 element named “branding.” We’ll simply set style for that element to use our newly imported font: #branding a { text-decoration:none; color: #434343; font-family: 'Frijole'; } Styling Projections Next we’re going to modify the event listing so that the event titles have a background color and text that’s in all caps. We could write our CSS selector expression to affect all a tags that follow h1 tags as that’s the way events are rendered, but such a selector would not be limited to events matching that pattern. Instead we’re going to inject a class name into each event row. In the admin dashboard, navigate to “Queries” and select the row for “All Events.” Click to edit the “1 columns grid” that we created previously. In the section with the heading “Html properties,” enter a value “event-row” under the “Row class” and save. Alternate Templates | 61 We could also have chosen from predefined dynamic expressions (in the drop-down menu for that field), but a static class is sufficient for our purposes. After you save the new row class, add a new CSS rule to affect a elements that follow h1 elements that follow a tr element with a class name of “event-row.” We’ll style the anchors to be displayed as “block” so that we have equal length backgrounds: tr.event-row h1 a { background-color:#BACEFF; padding:3px; color:#000; width:300px; display:block; } Figure 4-6 shows the template with our new styling. Shape Wrapping We’re going to add another template to our theme, but first we have to bring back the rest of our content customization. Copy or move Content-Bio.cshtml, Parts.Common.Body-11.cshtml, and Placement.info from “TheThemeMachine” to “DaisysTheme.” Figure 4-6. A styled projection 62 | Chapter 4: Creating Themes After moving those files, add a new template named NewsAndNotes.Wrapper.cshtml. Unlike our other templates, this one will surround its target with HTML and won’t actually modify the shape template itself. To call attention to band activity, the code for this wrapper will simply add a div element with a yellowish background to our “News and Notes” HTML widget:
An additional step is required for this wrapper to be used on our site. We need to update Placement.info to instruct Orchard to use this template. The match constraint will cause this rule to apply to the HTML widget on our home page: This scenario is admittedly slightly contrived, as we could have used an alternate tem- plate for our zone to accomplish the same thing. However, it does illustrate an addi- tional layer of customization available to theme designers. Theme Metadata We’ll consider our theme sufficiently styled at this point (at least by developer stand- ards). Now we’re ready to update the metadata used by the admin tool. Return to Visual Studio and open Theme.txt in the root of the theme. Most of the values are pretty obvious; Name, Author, Website, Description, and Version are included by default and shouldn’t merit any further description. If you personalize these values and return to Themes→Installed in the Dashboard, you’ll see these updated values. Theme Previews There are two final files you need to know about when developing themes: Theme.png and ThemeZonePreview.png. These files both live at the root of a theme. The former is typically a screen-grab of your theme’s homepage that will be used in the Dashboard and the gallery to provide a preview of your theme. The latter is an image used on the Widget admin page to provide a preview of where zones are conceptually placed in layout files. Theme Credits The last update we’ll want to make is to modify the chunk of HTML on the bottom of the page that gives credit to “TheThemeMachine” as this site’s theme. While that statement is partially true, we’ll instead claim credit for our “Daisy’sTheme” theme. We’ll need to override a file that’s in “TheThemeMachine” named BadgeOfHonor.cshtml. Theme Metadata | 63 Copy it from the Views directory of “TheThemeMachine” into our theme’s Views di- rectory. Modify the content so that the span with the “copyright” class has the content as follows: @T("© Daisy's Theme 2012.") Summary In our exploration of developing themes, we’ve seen that we don’t have to start from scratch when developing our site’s look and feel. In fact, this is a common way to develop themes. The “TheThemeMachine” theme provides a flexible, barebones layout that could easily be styled by a skilled designer. There is little reason to create new layout files and complex zone schemes when most of what you need may be found in this theme. Simply inherit from it and create a new stylesheet with new graphical treatments. Of course, your needs may require that you copy the entire “TheThemeMachine” theme to get started. Moreover, the same ap- proach we took to modifying “TheThemeMachine” applies to any theme you install into Orchard (theme license permitting). Figure 4-7. Daisy’s Theme with updated metadata 64 | Chapter 4: Creating Themes CHAPTER 5 Creating Modules We’ve already seen that Orchard may be extended easily and quickly by installing modules from the Gallery. When we wanted to add an image field to our event pages, we just installed the ImageField module. When we didn’t want to create our own con- tact form, we just installed one that someone had already written and shared. So what happens when we want functionality that isn’t readily available in the Gallery? Well, we just have to trade instant gratification for a little elbow grease. The Places Field Some of the most interesting data sources available to developers are those that contain places information. These are the databases that allow you to check into a business or some other venue with Facebook or Foursquare. When you arrive somewhere, your phone (or HTML5 capable browser) sends your location to a server-side API where a list of businesses, restaurants, and other public points of interest is then returned to the phone. You select the one where you are and check in. While it’s unlikely that users will be checking into the Daisy’s Gone site, there’s another use of places data of which we might wish to take advantage. Consider the case of Meetup.com, where users who want to schedule a Meetup for their group are asked to select a location before saving the new event. The location is chosen from an auto- suggested list of places, similar to the check-in features of Foursquare and Facebook. This is the functionality we’re going to add to our Event content type. Currently, our Location field is a text field into which event creators enter free-form text. In order to limit the locations to valid venues, we’ll instead use a field that auto-suggests possible places given a center point. To get this functionality, we’ll need a module. Fortunately for us, as of this writing, there is no such module in the Orchard Gallery. I say “fortu- nately” because we’re going to have the opportunity to create it in this chapter. 65 Getting Places Data There are several places where we could get our places data. Facebook and Foursquare provide places data that is somewhat coupled with their respective services. Google provides places data, but doesn’t allow that data to be rendered on a Bing Map (or any other non-Google Maps service). SimpleGeo provides places data, but it requires a paid developer key. For our needs, Yelp provides an API that will suit us well. Yelp offers a REST API that is easily consumed from a .NET application. For our project, we’ll use the YelpSharp library that’s available on Nuget. We’re not really con- cerned with the inner workings of a RESTful API client, so we’ll just install the client and use it as a black box. Module Code Generation When we created our new theme in Chapter 4, we used the Orchard.CodeGeneration module to create the initial directory structure for our theme’s files. The new files were added to the Themes project that is part of the main Orchard solution. Unlike themes, each module is housed in its own project by default. Within the solution, these projects are organized under the “Modules” solution folder. On disk, these projects are stored in the Modules directory beneath the Orchard.Web directory. Running the code generation command to create a new module will result in a new C# project being added to the solution within the Modules solution folder. If you’re familiar with ASP.NET MVC, you’ll probably recognize the structure, with familiar directories such as Views and Controllers. As we build out our module we’ll explore the project in more detail. Unlike the other modules that typically ship with Orchard, the code generated module projects are not web projects, but rather standard class library projects. What this difference means is that some of the context menus will not be web-centric (i.e., there is no “Add Control- ler”). As we did when creating our theme, we’ll use PowerShell (or the standard command line) to create an Orchard session. If you closed out your session from the previous chapter, navigate to the Orchard.Web project’s bin directory. Once there, execute Orchard.exe to initiate the session: orchard> codegen module Contrib.PlacesField Creating Module Contrib.PlacesField Module Contrib.PlacesField created successfully 66 | Chapter 5: Creating Modules The default behavior of the codegen utility is to add the new module project to the solution. So when you return to your solution in Visual Studio, you’ll be prompted to reload. Once you do, your new module project will be available. The module project is mostly comprised of empty directories and a few web.config files at this point. There’s also a Module.txt file that will describe our module to the admin Dashboard. Again, we’ll explore these project files as we start building out our new module. We’re going to create a content field, which is a little different than creating a more complex content part. Each has its own conventions. We’ll examine content part cre- ation in the next chapter on our way to creating a widget. However, some of the steps and concepts we’ll use while creating our field will certainly provide us with transferable knowledge when it comes time to create a more complex module. The Places Field Project Before we add any code to the project, let’s review the goal of our field to make sure we understand its requirements. At a high level, we simply want to limit the list of locations for an event to those that come from the Yelp API. To do so, we’ll ask admin users (or content creators) to provide some details that will narrow the places search. After providing those pieces of information, users may then select a place from the list. That place and its details will then be saved and made available for rendering on the event pages (or any other content items where this field is used). The Yelp API is highly flexible, allowing for searches by category, distance, search terms, and more. For the sake of keeping things simple our field won’t support all possible query options, though it should be sufficiently generic to allow for reuse. Places Field Model We’ll start off by considering the model for our field. Our model will represent the query and its result. To that end, we’ll create a model class that has properties for some of the possible search parameters that we’ll use when querying the API. This model class will be a subclass of Orchard’s ContentField, which will provide our class with a few basic services that we’ll see as we build our field. Under the new Contrib.PlacesField project, create a new directory named Fields and add a class to that directory named PlacesField.cs. This class is just a plain old CLR object (POCO) that uses the persistence capabilities of the Storage property from its base class to save and retrieve the values set by content creators: using Orchard.ContentManagement; namespace Contrib.PlacesField.Fields { public class PlacesField : ContentField { public string PostalCode { The Places Field Project | 67 get { return Storage.Get("PostalCode"); } set { Storage.Set("PostalCode", value); } } public string Category { get { return Storage.Get("Category"); } set { Storage.Set("Category", value); } } public string PlaceName { get { return Storage.Get("PlaceName"); } set { Storage.Set("PlaceName", value); } } public string PlaceLatLong { get { return Storage.Get("PlaceLatLong"); } set { Storage.Set("PlaceLatLong", value); } } } } In our admin pages, we’re going to collect information that is specific to a content item that is using our field, namely a postal code, category, and the selected place. We’re also going to collect settings from content type creators that will affect how all content items built from a content type definition behave. We’ll create the data model for our settings shortly and see how this all ties together when we update our Event type. We’ve just defined a mixed data model that combines type-level settings with item- level data. The settings and data will both be used to render places by changing the behavior of the view appropriately. To bridge the gap between our disparate models and the denormalized view, we’ll create a class that knows about both but more closely resembles the view. This approach is commonly known as the Model-View-ViewModel (MVVM) pattern, and it is frequently used in ASP.NET MVC applications. Create a new folder named ViewModels and add a file named PlacesFieldViewModel.cs. This class will look very similar to our actual model class, but it won’t concern itself with persistence. It will also have a simple string property for our category IDs and two additional properties for keeping track of our view options (the global settings): namespace Contrib.PlacesField.ViewModels { public class PlacesFieldViewModel { public string Name { get; set; } public string PostalCode { get; set; } public string Category { get; set; } public string PlaceName { get; set; } public string PlaceLatLong { get; set; } 68 | Chapter 5: Creating Modules public bool ShowLink { get; set; } public bool ShowMap { get; set; } } } As hinted at, we’re also going to include an option that will allow admins to decide which way to render the selected place on content pages. We’ll provide options for displaying only the name of the place, displaying the name with a link to Bing Maps, or with an embedded Bing Map (using an iframe element, not the widget). The way we’ll represent these options in our model will be to create a settings class. This class is just another POCO and will be used by the admin pages that manage content types that use our field. We’ll include a simple enumeration for the options. Create a new folder named Settings and add a file PlacesFieldSettings.cs with the fol- lowing content (you can include the enum definition in this file): namespace Contrib.PlacesField.Settings { public enum PlacesFieldDisplayOptions { NameOnly, NameAndLinkToMap, NameAndEmbeddedMap } public class PlacesFieldSettings { public PlacesFieldDisplayOptions DisplayOptions { get; set; } } } Drivers Now that we’ve created our supporting data classes, it’s time to write some code to take care of the actual rendering and saving of our field data. The class that has these responsibilities is known as a “driver.” We’ll also see drivers when we build a content part in Chapter 6. If you’re familiar with ASP.NET MVC, you could think of drivers as being analogous to controller classes that are tightly coupled to a module. Drivers will be responsible for rendering our field’s admin (when creating content) and content item (when dis- playing content) templates. Create a new directory named Drivers and add a file named PlacesFieldDriver.cs. This new class will extend ContentFieldDriver, which will allow Orchard to recognize that this module is in fact a content field. There are three methods we’ll override in this The Places Field Project | 69 class, which are discussed next. Though it certainly looks complex, the Display and Editor methods are simply either passing data to a view or reading data back from a form post: using Orchard.ContentManagement.Drivers; using Contrib.PlacesField.Settings; using Orchard.ContentManagement; using Contrib.PlacesField.ViewModels; namespace Contrib.PlacesField.Drivers { public class PlacesFieldDriver : ContentFieldDriver { //methods shown below } } The Display method begins by retrieving the saved settings for our field (we’ll write code to save these shortly). The ContentField class that we extended to create our PlacesField provides the functionality to retrieve these settings. After getting the set- tings, the ContentShape method is used to return a shape to the view that will be used for display. Note that this code won’t compile until we complete the rest of the dependent code we write later in this chapter, so don’t worry if you see several missing references. Recall that the content that we bind to our views is rendered as a special dynamic type called a shape. The lambda expression provided as our third argument to Content Shape is creating the actual shape that will be used as the model for our PlacesField view: protected override DriverResult Display(ContentPart part, Fields.PlacesField field, string displayType, dynamic shapeHelper) { var settings = field.PartFieldDefinition .Settings .GetModel(); return ContentShape("Fields_Contrib_Places", field.Name, s => s.Name(field.Name) .PlaceName(field.PlaceName) .PlaceLatLong(field.PlaceLatLong) .ShowLink(settings.DisplayOptions == PlacesFieldDisplayOptions.NameAndLinkToMap) .ShowMap(settings.DisplayOptions == PlacesFieldDisplayOptions.NameAndEmbeddedMap) ); } 70 | Chapter 5: Creating Modules C# 4.0 introduced the dynamic keyword, which when applied to a vari- able, instructs the compiler not to check for compile-time correctness of properties and methods. In other words, you are letting the compiler know that everything will evaluate correctly at runtime. Orchard makes heavy use of this dynamic feature (e.g., shapes). Without C# 4.0, Or- chard could not exist as it’s written today. The first Editor method is used to load our field into the dashboard editor forms for content types using our field (i.e., Create→New→Event). As was the case with Display, Editor also begins by retrieving the saved settings. It then composes a ViewModel in- stance from both settings and persisted field values. ContentShape is again called, but in this case, the EditorTemplate method of the dynamic shapeHelper is used to load our editor template and provide our ViewModel instance as the view’s Model property: protected override DriverResult Editor(ContentPart part, Fields.PlacesField field, dynamic shapeHelper) { var settings = field.PartFieldDefinition .Settings.GetModel(); var viewModel = new PlacesFieldViewModel { Name = field.Name, Category = field.Category, PostalCode = field.PostalCode, ShowLink = settings.DisplayOptions == PlacesFieldDisplayOptions.NameAndLinkToMap, ShowMap = settings.DisplayOptions == PlacesFieldDisplayOptions.NameAndEmbeddedMap, PlaceName = field.PlaceName, PlaceLatLong = field.PlaceLatLong }; return ContentShape("Fields_Contrib_Places_Edit", () => shapeHelper.EditorTemplate( TemplateName: "Fields/Contrib.Places", Model: viewModel, Prefix: getPrefix(field, part) )); } The second Editor method handles the post-back for our field when a user saves a content item that was defined to use our new field. After saving changes and mapping posted values to our field instance, the edit template is redisplayed (by way of the content item’s editor form): protected override DriverResult Editor(ContentPart part, Fields.PlacesField field, IUpdateModel updater, dynamic shapeHelper) { var viewModel = new PlacesFieldViewModel(); The Places Field Project | 71 if (updater.TryUpdateModel(viewModel, getPrefix(field, part), null, null)) { var settings = field.PartFieldDefinition .Settings.GetModel(); field.Category = viewModel.Category; field.PostalCode = viewModel.PostalCode; field.PlaceName = viewModel.PlaceName; field.PlaceLatLong = viewModel.PlaceLatLong; viewModel.ShowLink = settings.DisplayOptions == PlacesFieldDisplayOptions.NameAndLinkToMap; viewModel.ShowMap = settings.DisplayOptions == PlacesFieldDisplayOptions.NameAndEmbeddedMap; } return Editor(part, field, shapeHelper); } The private getPrefix method is used to create unique column values in the database for our field. It’s a convention to define this method, not a requirement: private static string getPrefix(ContentField field, ContentPart part) { return (part.PartDefinition.Name + "." + field.Name) .Replace(" ", "_"); } Field Templates Next we’re going to create some templates for our field. We’ll need both an editor template and a standard (non-admin) view template. We’ll start with the view template, because it is reasonably straightforward. In the Views directory, create a new directory named Fields and add to it a file named Contrib.Places.cshtml: @using Orchard.Utility.Extensions @{ string name = Model.Name; string mapLink = "http://www.bing.com/maps/?v=2&cp=" + @Model.PlaceLatLong + "&lvl=12&dir=0&sty=r&where1=" + @Model.PlaceLatLong.ToString().Replace("~", ","); string iframeSource = "http://www.bing.com/maps/embed/?v=2&cp=" + @Model.PlaceLatLong + "&lvl=15&dir=0&sty=r&where1=" + @Model.PlaceLatLong.ToString().Replace("~", ",") + 72 | Chapter 5: Creating Modules "&form=LMLTEW&pp=" + @Model.PlaceLatLong + "&emid=30e5aeb2-f963-4089-e9b3-9bd5dfe07759"; }

@name.CamelFriendly(): @if (!Model.ShowLink && ! Model.ShowMap) { @Model.PlaceName } @if (Model.ShowLink) { @Model.PlaceName } @if (Model.ShowMap) { @Model.PlaceName


After importing the Orchard.Utilities.Extensions namespace, we define three vari- ables to be used in the rendering of our field. Next, the display option chosen by the creator of the content type is interrogated to determine how we should display the place on our content item (e.g., an Event item). The logic here is fairly straightforward. The CamelFriendly extension method required declaring the string name variable since we can’t use extension methods with dynamic ex- pressions such as Model.Name. Next we’ll create the editor templates. In the Views directory, create a new directory named EditorTemplates with a subdirectory named Fields. Add to that directory a file named Contrib.Places.cshtml. This template will provide a simple UI for selecting a category, inputting a postal code and then selecting the place. We’ll skip validation for the sake of keeping the form from getting more complicated: @using Orchard.Utility.Extensions @model Contrib.PlacesField.ViewModels.PlacesFieldViewModel @{ Style.Require("jQueryUI_Orchard"); Script.Require("jQuery"); Script.Require("jQueryUtils"); Script.Require("jQueryUI_Core"); Script.Require("jQueryUI_Widget"); Script.Require("jQueryUI_Autocomplete"); }
The Places Field Project | 73

@Html.HiddenFor(m => m.Category)

@Html.EditorFor(m => m.PostalCode)

@Html.EditorFor(m => m.PlaceName) @Html.HiddenFor(m => m.PlaceLatLong)

@using (Script.Foot()) { } It might look like there’s a lot happening in this view, but it’s relatively straightforward. We’re using the jQuery UI Autocomplete plugin to render our short list of Yelp cate- gories. The autocomplete code for categories and places is in the script block at the bottom of our Razor template. How the Autocomplete plugin works isn’t important for understanding how to create a field, so we’ll skip a detailed explanation. The important thing to understand is that we’ve created a form with three fields for collecting information relevant to our places selection. We use hidden fields to save category and places data that is persisted, but not displayed to the user (category ID, latitude, and longitude). For Orchard to know where to put our field, we’ll need to create a Placement.info file at the root of our module project. The entries in this file will allow our templates to appear in both admin forms and content displays. We saw how placement files work in Displaying Content. Without this file, your field will not display: Settings Next we’ll take care of persisting the settings. We’re going to need to hook into admin editor events in order to save our settings. This code is mostly boilerplate and you’ll find yourself copying, pasting, and modifying it as you create your own fields that require settings. Start by creating a new file named PlacesFieldEditorEvents.cs in the Settings directory that we created earlier. In PartFieldEditor, we help Orchard load our settings form within the context of a content type editor form (we’ll see where this form is rendered before the end of the chapter). PartFieldEditorUpdate is used to handle the actual saving of our settings. The Places Field Project | 75 These two methods are similar to Editor and EditorTemplate in our field’s driver class: using System.Collections.Generic; using Contrib.PlacesField.Settings; using Orchard.ContentManagement; using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData.Builders; using Orchard.ContentManagement.MetaData.Models; using Orchard.ContentManagement.ViewModels; public class PlacesFieldEditorEvents : ContentDefinitionEditorEventsBase { public override IEnumerable PartFieldEditor(ContentPartFieldDefinition definition) { if (definition.FieldDefinition.Name == "PlacesField") { var model = definition.Settings.GetModel(); yield return DefinitionTemplate(model); } } public override IEnumerable PartFieldEditorUpdate( ContentPartFieldDefinitionBuilder builder, IUpdateModel updateModel) { var model = new PlacesFieldSettings(); if (builder.FieldType != "PlacesField") { yield break; } if (updateModel.TryUpdateModel( model, "PlacesFieldSettings", null, null)) { builder.WithSetting("PlacesFieldSettings.DisplayOptions", model.DisplayOptions.ToString()); } yield return DefinitionTemplate(model); } } To create the editor template for our settings, create a new file named PlacesFieldSet- tings.cshtml in a new directory under Views named DefinitionTemplates. Note that this template will appear when creating content types, not content items. It’s the creator of the type, not the content item that will set this value. In our template, we’ll simply render an HTML select list from the values in our PlacesFieldDisplayOptions enumeration: @model Contrib.PlacesField.Settings.PlacesFieldSettings @using Contrib.PlacesField.Settings;
@Html.ValidationMessageFor(m => m.DisplayOptions)
Controllers One final important detail is the data. So far, we’ve created a pretty complex field, but it won’t actually do anything until we provide it a way to get data from the Yelp API. As I previously mentioned, the API is a simple REST API. However, we can’t query it directly from our views, because browser security restrictions won’t allow AJAX re- quests to other servers. To solve this problem, we’ll have to create a solution that runs on our Orchard site. Specifically, we’re going to create an ASP.NET MVC controller class that will handle the AJAX requests that are generated by the jQuery Autocomplete plugin. If you revisit the code for our editor view, you’ll see the URLs that we’re going to call. URL to retrieve Yelp Categories /Admin/PlacesField/Yelp/Categories URL to retrieve Yelp Places /Admin/PlacesField/Yelp/Places If you’re familiar with MVC, you probably recognize that we’re going to create a con- troller named YelpController with two action methods, Categories and Places. We’re going to examine the controller shortly, but for now we need to setup a route to tell Orchard and MVC how to map our requests. Orchard will automatically map all routes by finding classes that implement IRouteProvider. Save this file as Routes.cs at the root of the module project: using System.Collections.Generic; using System.Web.Mvc; using System.Web.Routing; using Orchard.Mvc.Routes; public class Routes : IRouteProvider { public void GetRoutes(ICollection routes) { foreach (var routeDescriptor in GetRoutes()) routes.Add(routeDescriptor); } public IEnumerable GetRoutes() { The Places Field Project | 77 return new[] { new RouteDescriptor { Priority = 5, Route = new Route( "Admin/PlacesField/{controller}/{action}", new RouteValueDictionary { {"area", "Contrib.PlacesField"}, {"controller", "Yelp"}, {"action", "Places"} }, new RouteValueDictionary(), new RouteValueDictionary { {"area", "Contrib.PlacesField"} }, new MvcRouteHandler()) } }; } } The area defined in the RouteValueDictionary must be the name of the module and not an area that is defined as a URL part as is expected with typical MVC routing. The Controller class is a standard MVC controller. Save this file as YelpController.cs in the Controllers directory that the code generation tools created for the module project: using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using YelpSharp; using YelpSharp.Data.Options; public class YelpController : Controller { private readonly Yelp _client; public YelpController() { var options = new Options() { AccessToken = "", AccessTokenSecret = "", ConsumerKey = "", ConsumerSecret = "" }; _client = new Yelp(options); } public YelpController(Yelp client) { _client = client; } 78 | Chapter 5: Creating Modules public ActionResult Places(string postalCode, string category, string term) { var so = new SearchOptions() { LocationOptions = new LocationOptions() { location = postalCode }, GeneralOptions = new GeneralOptions(){ category_filter = category, term = term } }; var results = _client.Search(so); return Json(results.businesses.Select( p => new { value = p.location.coordinate.latitude + "~" + p. location.coordinate.longitude, label = p.name }).ToArray() , JsonRequestBehavior.AllowGet); } public ActionResult Categories(string postalCode, string categories, string term) { var categoryList = new List { "Restaurants", "Nightlife", "Arts" }; var results = string.IsNullOrEmpty(term) ? categoryList : categoryList .Where(c => c.ToLower() .Contains(term.ToLower())); return Json(results.Select( c => new { value = c.ToLower(), label = c }) .ToArray(), JsonRequestBehavior.AllowGet); } } Most of the logic in the controller deals with retrieving data from the Yelp service. For this code to work, you’ll need to add the YelpSharp Nuget package to your field project: PM> Install-Package YelpSharp You’ll need to get free API credentials from http://www.yelp.com/devel opers/getting_started. These API credentials then need to be entered into the constructor of the controller, where the Yelp client is instantiated. If you forget this step, your field won’t work. Request the 2.0 API on the API access page. It will give you all four keys that you’ll require. The Places Field Project | 79 Again, this class is a standard MVC controller. There isn’t anything Orchard-specific to consider here. Basically, all that’s happening in each action (public method that returns an ActionResult) is that we’re querying the Yelp API method and returning a JSON serialized result of the Yelp data. The YelpSharp client takes care of deserializing the Yelp response to POCO classes that we’ll then serialize to JSON formats that our autocomplete textboxes will consume. We use a LINQ projection to create a JSON structure that is friendly to the Autocom plete plugin. Module Metadata The final step is to set the metadata for our field in Module.txt. These settings are straightforward and include author and module description. If we had multiple fields in our project we could list multiple features, which could be enabled or disabled sep- arately. Finally, we’ll also declare a set of modules upon which our field is dependent: Name: Places Field AntiForgery: enabled Author: John Zablocki Website: http://dllhell.net Version: 1.0 OrchardVersion: 1.3 Description: The Places Field allows for location lookups Features: Contrib.PlacesField: Name: Places Field Description: Places fields. Category: Fields Dependencies: Orchard.jQuery, Common, Settings Using the Places Field Now that we’ve created our field, it’s time to compile the project and enable the module. Build your project to make sure you’ve successfully added all the using directives and Nuget references that our code demands. Then return to your Orchard command-line session and enable the new module: PM> feature enable Contrib.PlacesField Enabling features Contrib.PlacesField Places Field was enabled Creating Content with the Places Field Now that our field is coded and enabled, it’s time to test it out. Return to the Dashboard and select Content→Content Types and select “Edit” on the listing for “Event.” Click “Remove” on the field listing for the existing “Location” field. Then click “Add Field” and select the field type “Places Field.” Name the field “Event Location,” accepting the 80 | Chapter 5: Creating Modules default technical name as usual. Click “Save” and the “Event Location” field will appear in the list of fields. We could add several PlacesField elements to our Event content type if we wanted to, which is why we had to prefix our autocompleter client side IDs with field specific IDs. In other words, when you select a place for PlacesFieldA it won’t get mixed up with PlacesFieldB. Click “>” next to the field name to be presented with our settings editor. It’s here while defining our content type that we’ll set the display option for this field, affecting all items created from this content type definition. From the select list with our display options (Figure 5-1), choose “Name and Embedded Map” and click Save. Figure 5-1. The Places Field settings template If you were to browse to the “Events” listing page on our site, you’d see a Bing Maps error because we haven’t selected a place and we set our field to render a map. You should also see a Bing Maps error on your content item listing (Content→Content Items), because Orchard uses the same field display template to show a preview of your content item in the admin content listing page. That template includes the map code. Using the Places Field | 81 To fix these errors, we’ll simply select a place. Click Content→Content Items and then click “Edit” on the event listing you wish to edit. As you start typing in the “Category” field, you should see a list of categories that match your input. After selecting a category and entering a zip code, go ahead and search for a place by typing in the “Place” textbox. Yelp provides a full list of available categories that can be used to filter out results at http://www.yelp.com/developers/documentation/category _list. This module limits searches to only those categories that seem likely to host live music. A more robust solution would also provide some better caching and filtering of results. Figure 5-2. The Places Field editor template After you’ve selected a place, click “Publish Now” and then return to the site and refresh the “Events” page. You should now see the event location displayed with both the name of the place you selected and an embedded Bing map (Figure 5-3). 82 | Chapter 5: Creating Modules Figure 5-3. The Places Field displayed with a Bing map Displaying the Places Field If you click over to the home page (or any page that isn’t the “Events” page) you’ll see that our event location map is overtaking the space it’s been allotted in the AsideSec ond zone. Our “Upcoming Events” HTML widget is trying to use the embedded map in a smaller space than our event listing or event detail pages. In Chapter 3, we learned how to customize the display of fields and other types of content. Our own fields may be customized in the same way. Copy Contrib.Places.cshtml from the Fields directory under the Views directory in our mod- ule. Paste it in the Views directory of our “DaisysTheme” theme and rename it Fields.Contrib.Places.cshtml. Change the height and width of the iframe that includes the map and refresh the “Events” page. You’ll see that the embedded map has changed to reflect your chosen size: