How to Config MVCSiteMap to realize the parameters? - mvcsitemapprovider

I'm new to MVCSiteMap and I have a simple question:
I use the default route config like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Now in my controller, I want to create and edit an entity in the same Action:
public ActionResult AddEdit(int? id)
{}
so if the id is null, it means add, and if it is not null then the action is edit.
Now I want the site map to realize the different from add and edit. I tried this:
<mvcSiteMapNode title="Parent" controller="Class" action="Index">
<mvcSiteMapNode title="Add" controller="Class" action="AddEdit" />
<mvcSiteMapNode title="Edit" controller="Class" action="AddEdit" inheritedRouteParameters="Id"/>
</mvcSiteMapNode>
but seems it does not work well. It always use the second one.
What should I do?
Thanks a lot.

There are 2 options.
Option 1
Create a single node that sets preservedRouteParameters="id" on each of the nodes that correspond to an action method with a parameter. This creates a 1-to-1 relationship between the nodes and action methods, but a 1-to-many relationship between the node and the actual entities.
<mvcSiteMapNode title="Products" controller="Product" action="Index">
<mvcSiteMapNode title="Create New" controller="Product" action="Create" visibility="SiteMapPathHelper,!*" />
<mvcSiteMapNode title="Details" controller="Product" action="Details" visibility="SiteMapPathHelper,!*" preservedRouteParameters="id">
<mvcSiteMapNode title="Edit" controller="Product" action="Edit" visibility="SiteMapPathHelper,!*" key="Product_Edit" preservedRouteParameters="id"/>
<mvcSiteMapNode title="Delete" controller="Product" action="Delete" visibility="SiteMapPathHelper,!*" preservedRouteParameters="id"/>
</mvcSiteMapNode>
</mvcSiteMapNode>
This is the recommended way to do it if you are creating pages that edit data, especially if those pages will never be indexed by search engines.
In most cases, you will also need to setup the FilteredSiteMapNodeVisibilityProvider and SiteMapTitleAttribute to fix the visibility and title of the nodes. You won't be able to use this method for anything other than a breadcrumb trail, so it is important to hide these fake nodes from the other HTML helpers like the Menu and SiteMap.
For a complete demo of how this can be done, visit How to Make MvcSiteMapProvider Remember a User's Position.
Option 2
Use a custom IDynamicNodeProvider to create a node per entity (1-to-1 relationship).
public class StoreDetailsDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
using (var storeDB = new MusicStoreEntities())
{
// Create a node for each album
foreach (var album in storeDB.Albums.Include("Genre"))
{
DynamicNode dynamicNode = new DynamicNode();
dynamicNode.Title = album.Title;
dynamicNode.ParentKey = "Genre_" + album.Genre.Name;
dynamicNode.RouteValues.Add("id", album.AlbumId);
yield return dynamicNode;
}
}
}
}
To use this, you need to ensure you set up your key and parent keys in code so each node understands what parent node it belongs to. You may need to explicitly set the "key" attribute in your XML in order to do this. You also need to ensure you set the "id" routeValue on each record to ensure your node matches your incoming route.
Use this method when your pages must be indexed by search engines and/or you want to see the nodes in the menu.
Do note that you can combine these 2 options in the same application and it will work fine. Both of these methods will also work for any number of custom route values (other than "id") as well.

Related

Show dynamic breadcrumb based on preservedRouteParameters for MvcSiteMapProvider

I'm using MvcSiteMapProvider for ASP.NET MVC5 project. I want to show a dynamic breadcrumb based on preservedRouteUrlParamters. I have multiple universities and each university has courses. I don't want to list all the university in the mvc.sitemap.
Instead of:
url: /stanford
breadcrumb: home / university
url: /stanford/course1
breadcrumb: home / university / course details
It should look like:
url: /stanford
breadcrumb: home / stanford
url: /stanford/course1
breadcrumb: home / stanford / course details ...where stanford is link to /stanford
url: /mit
breadcrumb: home / mit
url: /mit/course1
breadcrumb: home / mit / course details ...where mit is link to /mit
So this is the pattern:
url: /{university}
breadcrumb: home / {university}
url: /{university}/{course}
breadcrumb: home / {university} / course details
Here is the mvc.sitemap config I have:
<mvcSiteMapNode title="university" controller="Curriculum" action="UniversityDetails" preservedRouteParameters="university">
<mvcSiteMapNode title="course details" action="CourseDetails" preservedRouteParameters="university,course"/>
</mvcSiteMapNode>
This is solution I have so far but I'm not sure if it is a good way.
I use title="{university}" and check for the pattern {university}.
<mvcSiteMapNode title="{university}" controller="Curriculum" action="UniversityDetails" preservedRouteParameters="university">
<mvcSiteMapNode title="course details" action="CourseDetails" preservedRouteParameters="university,course"/>
</mvcSiteMapNode>
I use the SiteMapNodeModel.Url to dynamically generate the breadcrumb.
public static string TitleBreadcrumb(this SiteMapNodeModel m)
{
if (m.Title.StartsWith("{") && m.Title.EndsWith("}"))
{
return m.Url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Last();
}
return m.Title;
}
I then use the above extension method in the SiteMapNodeModel.cshtml display template.
// use #Model.TitleBreadcrumb() instead of #Model.Title
#Model.TitleBreadcrumb()
Similar for #Model.Description.
Is there a better way?
The only thing particularly wrong with your approach is that you are not encoding the value from the URL before displaying it in your HTML. This means that some malicious user could potentially inject HTML and/or JavaScript into your page by manipulating the URL.
However, the most common way to provide a dynamic title is to use the SiteMapTitleAttribute, which uses a value from your Model or a value in your ViewData to populate the title dynamically.
[SiteMapTitle("Name")]
public ViewResult UniversityDetails(string university) {
var model = _repository.Find(university);
// Name is a string property of
// the university model object.
return View(model);
}
[SiteMapTitle("Name", Target = AttributeTarget.ParentNode)]
public ViewResult CourseDetails(string university) {
var model = _repository.Find(university);
// Name is a string property of
// the university model object.
return View(model);
}

Orchard CMS - Extending Users with Fields - exposing values in Blog Post

I'd like to extend the users content definition to include a short bio and picture that can be viewed on every blog post of an existing blog. I'm unsure of what the best method to do this is.
I have tried extending the User content type with those fields, but I can't seem to see them in the Model using the shape tracing tool on the front end.
Is there a way to pass through fields on the User shape in a blog post? If so, what is the best way to do it?
I also have done this a lot, and always include some custom functionality to achieve this.
There is a way to do this OOTB, but it's not the best IMO. You always have the 'Owner' property on the CommonPart of any content item, so in your blogpost view you can do this:
#{
var owner = Model.ContentItem.CommonPart.Owner;
}
<!-- This automatically builds anything that is attached to the user, except for what's in the UserPart (email, username, ..) -->
<h4>#owner.UserName</h4>
#Display(BuildDisplay((IUser) owner))
<!-- Or, with specific properties: -->
<h1>#T("Author:")</h1>
<h4>#owner.UserName</h4>
<label>#T("Biography")</label>
<p>
#Html.Raw(owner.BodyPart.Text)
</p>
<!-- <owner content item>.<Part with the image field>.<Name of the image field>.FirstMediaUrl (assuming you use MediaLibraryPickerField) -->
<img src="#owner.User.Image.FirstMediaUrl" />
What I often do though is creating a custom driver for this, so you can make use of placement.info and follow the orchard's best practices:
CommonPartDriver:
public class CommonPartDriver : ContentPartDriver<CommonPart> {
protected override DriverResult Display(CommonPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Common_Owner", () => {
if (part.Owner == null)
return null;
var ownerShape = _contentManager.BuildDisplay(part.Owner);
return shapeHelper.Parts_Common_Owner(Owner: part.Owner, OwnerShape: ownerShape);
});
}
}
Views/Parts.Common.Owner.cshtml:
<h1>#T("Author")</h1>
<h3>#Model.Owner.UserName</h3>
#Display(Model.OwnerShape)
Placement.info:
<Placement>
<!-- Place in aside second zone -->
<Place Parts_Common_Owner="/AsideSecond:before" />
</Placement>
IMHO the best way to have a simple extension on an Orchard user, is to create a ContentPart, e.g. "UserExtensions", and attach it to the Orchard user.
This UserExtensions part can then hold your fields, etc.
This way, your extensions are clearly separated from the core user.
To access this part and its fields in the front-end, just add an alternate for the particular view you want to override.
Is there a way to pass through fields on the User shape in a blog post?
Do you want to display a nice picture / vita / whatever of the blog posts author? If so:
This could be your Content-BlogPost.Detail.cshtml - Alternate
#using Orchard.Blogs.Models
#using Orchard.MediaLibrary.Fields
#using Orchard.Users.Models
#using Orchard.Utility.Extensions
#{
// Standard Orchard stuff here...
if ( Model.Title != null )
{
Layout.Title = Model.Title;
}
Model.Classes.Add("content-item");
var contentTypeClassName = ( (string)Model.ContentItem.ContentType ).HtmlClassify();
Model.Classes.Add(contentTypeClassName);
var tag = Tag(Model, "article");
// And here we go:
// Get the blogPost
var blogPostPart = (BlogPostPart)Model.ContentItem.BlogPostPart;
// Either access the creator directly
var blogPostAuthor = blogPostPart.Creator;
// Or go this way
var blogPostAuthorAsUserPart = ( (dynamic)blogPostPart.ContentItem ).UserPart as UserPart;
// Access your UserExtensions part
var userExtensions = ( (dynamic)blogPostAuthor.ContentItem ).UserExtensions;
// profit
var profilePicture = (MediaLibraryPickerField)userExtensions.ProfilePicture;
}
#tag.StartElement
<header>
#Display(Model.Header)
#if ( Model.Meta != null )
{
<div class="metadata">
#Display(Model.Meta)
</div>
}
<div class="author">
<img src="#profilePicture.FirstMediaUrl"/>
</div>
</header>
#Display(Model.Content)
#if ( Model.Footer != null )
{
<footer>
#Display(Model.Footer)
</footer>
}
#tag.EndElement
Hope this helps, here's the proof:

Upgrading to V4 has caused #Html.MvcSiteMap().SiteMapPath() to not return anything

Before I revert to the earlier package, does anybody know why this can be? I followed this guide pretty much to the letter.
https://github.com/maartenba/MvcSiteMapProvider/wiki/Upgrading-from-v3-to-v4
Cheers,
J
mvc.sitemap
<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMapNode title="Home" controller="Home" action="Index">
<mvcSiteMapNode title="Opportunity Stream" controller="TaskStream" action="Index"/>
<mvcSiteMapNode title="Opportunity Stream" controller="TaskStream" action="IndexNew"/>
<mvcSiteMapNode title="Appointment" controller="Appointment" action="Index"/>
<mvcSiteMapNode title="Vehicle Search" controller="VehicleSearch" action="Index"/>
<mvcSiteMapNode title="Stock" controller="Stock" action="Index"/>
<mvcSiteMapNode title="Admin" controller="Admin" action="Index">
<mvcSiteMapNode title="Team Management" controller="Admin" action="TeamManagement">
<mvcSiteMapNode title="Manage Team Member" controller="Admin" action="TeamManagementDetails"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Site Management" controller="Site" action="Index">
<mvcSiteMapNode title="Site" controller="Site" action="SiteOptions" preservedRouteParameters="id">
<mvcSiteMapNode title="Default Calendar" controller="Site" action="DefaultCalendar"/>
<mvcSiteMapNode title="Exception Calendar" controller="Site" action="ExceptionCalendar"/>
<mvcSiteMapNode title="Manage Site" controller="Site" action="Details"/>
<mvcSiteMapNode title="Manage Site" controller="Site" action="Edit"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Create Site" controller="Site" action="Create"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Approve Leave Requests" controller="LeaveRequest" action="Index"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Auction" controller="Auction" action="Index"/>
<mvcSiteMapNode title="Employee" controller="Employee" action="Index">
<mvcSiteMapNode title="Calendar Exceptions" controller="Site" action="TeamExceptions"/>
<mvcSiteMapNode title="Employee Detail" controller="Employee" action="Detail" clickable="false"/>
<mvcSiteMapNode title="Employee Detail" controller="Employee" action="Edit" clickable="false"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="User Profile" controller="UserProfile" action="Index">
<mvcSiteMapNode title="My Holidays" controller="UserProfile" action="MyHolidays"/>
<mvcSiteMapNode title="Create Leave Request" controller="LeaveRequest" action="Create"/>
</mvcSiteMapNode>
</mvcSiteMapNode>
RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
XmlSiteMapController.RegisterRoutes(RouteTable.Routes);
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*alljs}", new
{
alljs = #".*\.js(/.*)?"
});
routes.IgnoreRoute("{*allpng}", new
{
allpng = #".*\.png(/.*)?"
});
routes.IgnoreRoute("{*allcss}", new
{
allcss = #".*\.css(/.*)?"
});
routes.IgnoreRoute("{*allgif}", new
{
allgif = #".*\.gif(/.*)?"
});
routes.IgnoreRoute("{*alljpg}", new
{
alljpg = #".*\.jpg(/.*)?"
});
routes.MapRoute("Default", "{controller}/{action}/{id}",
new
{
country = "uk",
lang = "En",
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
routes.MapRoute("localizedDefault", "{country}/{lang}/{controller}/{action}/{id}",
new
{
country = "uk",
lang = "En",
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
}
The most likely cause is that you haven't accounted for the country or lang route parameters in your configuration. And since these are ambient values that have nothing to do with page identification, you could use preservedRouteParamters to force them to always match.
<mvcSiteMapNode title="Home" controller="Home" action="Index" preservedRouteParameters="country,lang">
Currently there is no way to specify preservedRouteParameters globally, so you will need to supply this attribute on every node.
However, be aware that your localized pages won't appear in the XML sitemap endpoint at /sitemap.xml when using preservedRouteParameters.
To fix that, you should fix your route configuration so they will instead appear at {country}/{lang}/sitemap.xml. Add the 2 routes to your configuration to create localized sitemap.xml endpoints, like this:
public static void RegisterRoutes(RouteCollection routes)
{
XmlSiteMapController.RegisterRoutes(RouteTable.Routes);
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*alljs}", new
{
alljs = #".*\.js(/.*)?"
});
routes.IgnoreRoute("{*allpng}", new
{
allpng = #".*\.png(/.*)?"
});
routes.IgnoreRoute("{*allcss}", new
{
allcss = #".*\.css(/.*)?"
});
routes.IgnoreRoute("{*allgif}", new
{
allgif = #".*\.gif(/.*)?"
});
routes.IgnoreRoute("{*alljpg}", new
{
alljpg = #".*\.jpg(/.*)?"
});
// Localized XML Sitemap routes for MvcSiteMapProvider
routes.MapRoute("localizedSitemap", "{country}/{lang}/sitemap.xml",
new
{
country = "uk",
lang = "En",
controller = "XmlSiteMap",
action = "Index",
page = 0
});
routes.MapRoute("localizedSitemapPage", "{country}/{lang}/sitemap-{page}.xml",
new
{
country = "uk",
lang = "En",
controller = "XmlSiteMap",
action = "Index",
page = 0
});
routes.MapRoute("Default", "{controller}/{action}/{id}",
new
{
country = "uk",
lang = "En",
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
routes.MapRoute("localizedDefault", "{country}/{lang}/{controller}/{action}/{id}",
new
{
country = "uk",
lang = "En",
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
}
You will need to create an index file so the search engines will know about your localized URLs, and submit the index file to search engines instead of the one at /sitemap.xml.
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<!-- specifies the default culture sitemap En-uk -->
<loc>http://www.example.com/sitemap.xml</loc>
</sitemap>
<sitemap>
<loc>http://www.example.com/de/De/sitemap.xml</loc>
</sitemap>
<sitemap>
<loc>http://www.example.com/mx/Es/sitemap.xml</loc>
</sitemap>
</sitemapindex>
You should also specify this sitemap index in your robots.txt file so bots of minor search engines can pick it up automatically. For example, if you put the above XML into a file named sitemapindex.xml at the root of your site (where it should be), you can add this line to your robots.txt file (anywhere in the file):
Sitemap: http://www.example.com/sitemapindex.xml
Reference: http://www.sitemaps.org/protocol.html#informing
You should also do a 301 redirect for requests of /uk/En/SomeController/SomeAction and /uk/En/SomeController/SomeAction/SomeId to the default versions of these URLs. This can be accomplished either by subclassing RouteBase to make a redirect route or by using an IIS rewrite rule in your web.config file.
Additional Information That Might Be Helpful
There are several things that could cause the SiteMapPath not to appear.
The node intended to match the current page is missing some route or querystring parameters.
Security Trimming is enabled and the user doesn't have permission to view the current node.
The visibility settings are prohibiting the nodes from being rendered.
The HTML helper templates in your /Views/Shared/DisplayTemplates/ folder are prohibiting them from being shown.
The configuration isn't set up to start automatically.
Since you are upgrading, the most likely causes are #1 or #5.
You can rule out #5 by navigating to /sitemap.xml to see if your XML sitemap is rendering.
It might be helpful to temporarily add #Html.MvcSiteMap().SiteMap() to your layout page to see if any nodes are not resolving to the correct URL.
If you do that, #1 is the most likely cause. You must configure every route value to either a specific value...
<mvcSiteMapNode title="Foo" action="Index" controller="Customer" id="3"/>
This works best in conjunction with dynamic node providers.
Or you can force a match with any value by using preservedRouteParameters.
<mvcSiteMapNode title="Foo" action="Index" controller="Customer" preservedRouteParameters="id" />
Have a look at How to Make MvcSiteMapProvider Remember a User's Position for an in depth discussion of this behavior.
Also, keep in mind that if you are using custom attributes that you don't intend to use with routing, you need to add them to the MvcSiteMapProvider_AttributesToIgnore configuration setting.
<mvcSiteMapNode title="Foo" action="Index" controller="Customer" myCustomValue="Something" />
In web.config:
<appSettings>
<add key="MvcSiteMapProvider_AttributesToIgnore" value="myCustomValue"/>
</appSettings>

Create child nodes dynamically for a node in MVC.Sitemap

I am working on an MVC 4 application where I am showing a menu on masterpage using Mvc.sitemap. I have a node named say "Tasks" which will appear everytime among other nodes on menu. I need to create child nodes for this node based on values fetched from database. Depending on number of values the child nodes will be created and on clicking each child code a certain function will be performed.
Since I dont know how to generate child nodes according to values from database, I have hardcoded nodes as of now in Mvc.sitemap. Below is the code of how I have been doing it now:
<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0"
xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0 MvcSiteMapSchema.xsd">
<mvcSiteMapNode title="Home" controller="Home" action="Index">
<mvcSiteMapNode title="Tasks" controller="Home" action="Index">
<mvcSiteMapNode title="Task 1" controller="Home" action="Index" url="http://localhost:...."/>
<mvcSiteMapNode title="Task 2" controller="Home" action="Index" url="http://localhost:...."/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Admin" controller="Home" action="Admin"/>
<mvcSiteMapNode title="About" controller="Home" action="About"/>
<mvcSiteMapNode title="Help" controller="Home" action="Help"/>
</mvcSiteMapNode>
</mvcSiteMap>
As you can see in the above code I have hardcoded the child nodes and also specified the url property.
Please help on how to achieve this dynamically. Thanks in Advance!!
This is what dynamic node providers are for.
<mvcSiteMapNode title="Tasks" controller="Home" action="Index" key="TasksIndex">
<!-- This is the task template node - this node won't be added to the SiteMap,
but will be used to define the defaults of the Dynamic Nodes -->
<mvcSiteMapNode action="Index" dynamicNodeProvider="MyNamespace.TaskDynamicNodeProvider, MyAssembly" />
</mvcSiteMapNode>
namespace MyNamespace
{
public class TaskDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
using (var db = new MyEntities())
{
// Create a node for each album
foreach (var task in db.Tasks)
{
var dynamicNode = new DynamicNode();
dynamicNode.Title = task.Name;
dynamicNode.ParentKey = "TasksIndex";
dynamicNode.RouteValues.Add("id", task.Id);
// NOTE: Controller is automatically inherited in the XML from the
// nearest parent node where it is set, and action is set in the
// template node in this example. However, you can override the
// values here if you need to.
// dynamicNode.Controller = "Home";
// dynamicNode.Action = "Index";
yield return dynamicNode;
}
}
}
}
}

Craeting Custom Widget in Orchard 1.7

I am new to Orchard. So please forgive me if there is anything looking silly!
I want to create a custom widget for my Orchard website to encourage visitors to sign up for my Newsletter service. I have seen there is an option of using HTML widget but I want to create a new widget type like "Newsletter" which I shall use conditionally at AsideFirst block.
Is this possible to do? I only want to grab visitor's Name and Email address, and the form submission will be done using an action controller.
Do I have to create this widget through by-hand coding in VS? In fact I want to this way, not through the Orchard admin console.
Seeking for help. Any suggestion please?
Edit:
I have managed to create the widget following Sipke Schoorstra's suggestion. The area where I want to display the widget is now showing along with the the title I set from admin at the time of adding it to a zone. But the content (form elements) I created in the view is not displaying.
The View: (Views/NewsLetterSignupPart/NewsletterSignup.cshtml)
#model Emfluence.Intrust.Models.NewsLetterSignupPart
#{
ViewBag.Title = "Newsletter Signup";
}
#using (Html.BeginForm("NewsletterSignup", "NewsLetter", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="row-fluid">
<div class="span6">
<label>Name</label>
<input type="text" name="txtNewsletterUserName" required maxlength="50" style="width: 95%" />
<label>Email</label>
<input name="txtNewsletterUserEmail" type="email" required maxlength="85" style="width: 95%" />
<button class="btn pull-right">Submit</button>
</div>
</div>
}
Migration.cs
public int UpdateFrom15()
{
ContentDefinitionManager.AlterTypeDefinition(
"NewsletterWidget", cfg => cfg
.WithPart("NewsLetterSignupPart")
.WithPart("CommonPart")
.WithPart("WidgetPart")
.WithSetting("Stereotype", "Widget")
);
return 16;
}
NewsLetterSignupPart.cs
public class NewsLetterSignupPart : ContentPart<NewsletterSignupRecord>
{
[Required]
public string Name
{
get { return Record.Name; }
set { Record.Name = value; }
}
[Required]
public string Email
{
get { return Record.Email; }
set { Record.Email = value; }
}
}
And NewsletterSignupRecord.cs
public class NewsletterSignupRecord : ContentPartRecord
{
public virtual string Name { get; set; }
public virtual string Email { get; set; }
}
Where I am doing wrong?
The Custom Forms module is great if you don't want or need to code something yourself. In case you do want to handle form submissions yourself without using Custom Forms, this is what you could do:
Create a custom module
Create a migrations class that defines a new widget content type (see the docs for details on how to do this. Note: you don't need to create a custom part. You don't even need to create a migrations file to create a content type - you could do it using a recipe file. The nice thing about a migration though is that it will execute automatically when your module's feature is enabled).
Create a view specific for content items of your widget type (e.g. Widget-Newsletter.cshtml).
Inside of this view, write markup that includes a form element and input elements. Have this form post back to your controller.
Create your controller.
In the /admin interface, click Modules, on the Features` tab search for Custom Forms and click Enable. This will add a new Forms admin link on the left.
Next, create a custom content type (under Content Definition) called Newsletter, and add two fields (of type Text Field) called Name and E-mail.
Finally, click Forms and add a new Custom Form. Give it a title: this will be the default URL to access e.g. "Newsletter Form" will have a URL of /newsletter-form by Orchard defaults. Under Content Type select your newly created content type, Newsletter, from the dropdown. Customize anything else you want on this page, and click Publish Now
If you want to make this a widget, edit the content type and add the Widget Part. Create a layer with the rules you need and you can add the "Newsletter" widget to any zone you need on that layer.

Resources