The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.
So, you’ve created a custom module, great! Now what? Well, now it’s time to get some data in there. Sitefinity’s Module Builder generates code samples that you can use to start interacting with custom content types right away. In this
post, I’m going to show you how I used those samples to help import a list of store locations to the Quantum Computers website.
No matter how many code samples Module Builder gives us, we’re still going to need a way to read our source data. Usually, my source data comes in CSV form and my favorite way to interact with those is by using the FileHelpers library found on SourceForge. (I’m using version 2.0 for this example.)
I’m only going to cover some simple usage of the FileHelpers library. Please take a look at the project documentation for more in-depth information.
To start using the FileHelpers Library download the .zip and add a reference to FileHelpers.dll in your project. You can find it in the Release directory of the distribution.
After adding that reference, we can start building our import script. The fastest way to get started is by creating a quick Web Form in the root of the Sitefinity project. That will provide a perfect place to start coding quickly and without any fuss.
We need to define a class that maps to our source data in order to use FileHelpers. The properties of that class need to be in the same order that the values appear in the CSV. Here’s a sample line from the stores.csv file I'm using and what the matching class will look like:
Quantum Los Angeles West,3611 W 30th St,Los Angeles,90018,US,CA,store5.jpg,+1-800-213-3407
[DelimitedRecord(
","
)]
public
class
Store
{
public
string
Title;
public
string
Street;
public
string
City;
public
string
Zip;
public
string
Country;
public
string
State;
public
string
StoreImage;
public
string
Phone;
}
Now it’s time to actually read the file. For this example, I’ve placed it in the App_Data folder of the Sitefinity project. Here’s the snippet to read it:
FileHelperEngine engine =
new
FileHelperEngine(
typeof
(Store));
// To Read Use:
Store[] stores = engine.ReadFile(HttpContext.Current.Server.MapPath(
"~/App_Data/stores.csv"
))
as
Store[];
After that code runs, we’ll have all the information we need to start creating our Sitefinity items. The best part is that now we don’t have to do a lot of crazy data parsing on our CSV data. FileHelpers did it for us!
Now that we’ve got an array full of Store objects, let’s loop through it and start creating some Sitefinity content. This is where the code reference that Sitefinity provides eliminates almost all of our remaining coding effort. I’ve copied the code from the “Create a Store” section of the code reference and used it to create the following method.
// Creates a new store item
public
void
CreateStore(Store s)
{
// Set the provider name for the DynamicModuleManager here. All available providers are listed in
// Administration -> Settings -> Advanced -> DynamicModules -> Providers
var providerName = String.Empty;
// Set the culture name for the multilingual fields
var cultureName =
"en"
;
Thread.CurrentThread.CurrentUICulture =
new
CultureInfo(cultureName);
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(providerName);
Type storeType = TypeResolutionService.ResolveType(
"Telerik.Sitefinity.DynamicTypes.Model.Stores.Store"
);
DynamicContent storeItem = dynamicModuleManager.CreateDataItem(storeType);
// This is how values for the properties are set
storeItem.SetString(
"Title"
, s.Title, cultureName);
storeItem.SetString(
"Phone"
, s.Phone, cultureName);
//LibrariesManager libraryManager = LibrariesManager.GetManager();
//var image = libraryManager.GetImages().FirstOrDefault(i => i.FilePath == s.StoreImage && i.Status == Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live);
// I commented out the default code above. This is a useful way to find images by filename
var image = App.WorkWith().Images().Where(i => i.Status == ContentLifecycleStatus.Master).Get().ToList().Where(i => Path.GetFileName(i.FilePath) == s.StoreImage).FirstOrDefault();
if
(image !=
null
)
{
storeItem.AddImage(
"StoreImage"
, image.Id);
}
Address address =
new
Address();
CountryElement addressCountry = Config.Get<LocationsConfig>().Countries.Values.First(x => x.Name ==
"United States"
);
address.CountryCode = addressCountry.IsoCode;
address.StateCode = s.State;
address.City = s.City;
address.Street = s.Street;
address.Zip = s.Zip;
// This was added to get Geo Location coordinates from Google
GeoCoordinate geoValue = GetGeoCoordinates(s.Street +
", "
+ s.City +
", "
+ s.State +
" "
+ s.Zip +
", "
+ s.Country);
address.Latitude = geoValue.Latitude;
address.Longitude = geoValue.Longitude;
address.MapZoomLevel = 8;
storeItem.SetValue(
"Address"
, address);
// modified a bit to give us a good Url Name
storeItem.SetString(
"UrlName"
, Regex.Replace(s.Title.ToLower(), @
"[^\w\-\!\$\'\(\)\=\@\d_]+"
,
"-"
), cultureName);
storeItem.SetValue(
"Owner"
, SecurityManager.GetCurrentUserId());
storeItem.SetValue(
"PublicationDate"
, DateTime.Now);
// Modified to publish instead of set items as draft
storeItem.SetWorkflowStatus(dynamicModuleManager.Provider.ApplicationName,
"Published"
,
new
CultureInfo(cultureName));
// You need to call SaveChanges() in order for the items to be actually persisted to data store
dynamicModuleManager.SaveChanges();
// Use lifecycle so that LanguageData and other Multilingual related values are correctly created
DynamicContent checkOutStoreItem = dynamicModuleManager.Lifecycle.CheckOut(storeItem)
as
DynamicContent;
dynamicModuleManager.Lifecycle.CheckIn(checkOutStoreItem);
dynamicModuleManager.SaveChanges();
}
I want to point out the call to “GetGeoCoordinates”. That’s a method I pieced together that calls Google and requests the latitude and longitude for a given address. That wasn't provided for us but I’ve included it (along with a convenience class for passing the coordinates back) for reference below.
public
GeoCoordinate GetGeoCoordinates(
string
address)
{
string
googleAPI =
"http://maps.googleapis.com/maps/api/geocode/xml?address="
+ HttpUtility.UrlEncode(address) +
"&sensor=false"
;
GeoCoordinate geoCoord =
new
GeoCoordinate();
XmlDocument objXmlDocument =
new
XmlDocument();
objXmlDocument.Load(googleAPI);
XmlNodeList objXmlNodeList = objXmlDocument.SelectNodes(
"/GeocodeResponse/result/geometry/location"
);
foreach
(XmlNode objXmlNode
in
objXmlNodeList)
{
geoCoord.Latitude =
double
.Parse(objXmlNode.ChildNodes.Item(0).InnerText);
geoCoord.Longitude =
double
.Parse(objXmlNode.ChildNodes.Item(1).InnerText);
}
return
geoCoord;
}
// This is for convenience only
public
struct
GeoCoordinate
{
public
double
Latitude {
get
;
set
; }
public
double
Longitude {
get
;
set
; }
}
There are a lot of comments included by default and they do a good job of pointing out what’s going on. There are a few that I added to explain areas where I tweaked the code.
All that’s left to do is build the solution and make a quick visit to our script via a browser. I named the script "StoreImport.aspx" so i just need to visit "~/StoreImport.aspx" to run it. Once our code finishes, click back over to the module to see all the items we just created.
And there we have it. Minus a few lines of code for accessing our external data and the Google API, we just populated a custom module using a bunch of code we didn’t have to write!
I invite you to join me on December 10th for my webinar on Module Builder where I'll be diving deeper into the code above and handing
out tips on using it to be more productive.
View all posts from Tim Williamson on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.
Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.
Learn MoreSubscribe to get all the news, info and tutorials you need to build better business apps and sites
Progress collects the Personal Information set out in our Privacy Policy and the Supplemental Privacy notice for residents of California and other US States and uses it for the purposes stated in that policy.
You can also ask us not to share your Personal Information to third parties here: Do Not Sell or Share My Info
We see that you have already chosen to receive marketing materials from us. If you wish to change this at any time you may do so by clicking here.
Thank you for your continued interest in Progress. Based on either your previous activity on our websites or our ongoing relationship, we will keep you updated on our products, solutions, services, company news and events. If you decide that you want to be removed from our mailing lists at any time, you can change your contact preferences by clicking here.