Episerver – Could not find stored procedure ‘netCategoryListAll’

My scenario
I tried creating a new database schema for my Episerver website that uses the createDatabaseSchema=”true” on the episerver.framework config node. This creates a new database schema if it detects there’s none for the database specified in the config. However, when I first ran my Episerver website which was configured to an empty database, I got the following error:

server error could not find netCategoryListAll

Solution
Run the following scripts:

  • %systemroot%\Microsoft.NET\Framework64\v4.0.30319\SQL\en\SqlPersistenceService_Schema.sql
  • %systemroot%\Microsoft.NET\Framework64\v4.0.30319\SQL\en\SqlPersistenceService_Logic.sql
  • [your site installation folder]\packages\EPiServer.CMS.Core.7.7.1\tools\EPiServer.Cms.Core.sql

Please note the third script above can be taken from the NuGet package installed on your website’s packages. If you don’t have this, you can create an empty Episerver website in Visual Studio using the Episerver templates which will create a packages folder that contains this script.

References
Dan Matthews blog: http://world.episerver.com/blogs/Dan-Matthews/Dates/2014/10/Database-failures-when-creating-a-site-in-Visual-Studio/

 

 

How to order tabs of properties for a page type

If you have quite a complicated page/block type where you end up having more than 5 tabs of properties, then you start thinking of sorting these tabs too.

To achieve this, make sure to use the attribute [GroupDefinition] and specify the Order.

 [GroupDefinitions]
 public static class MyGroupNames

 {
    [Display(Order = 50)]
    public const string Banner = "Banner";

    [Display(Order = 101)]
    public const string Tab1 = "Tab 1";

    [Display(Order = 102)]
    public const string Tab2 = "Tab 2";

    [Display(Order = 103)]
    public const string Tab3 = "Tab 3";

    [Display(Order = 104)]
    public const string Tab4 = "Tab 4";

    [Display(Order = 105)]
    public const string Tab5 = "Tab 5";

    [Display(Order = 300)]
    public const string Sidebar = "Sidebar";
 }

Then use these TabNames on your property.

 [CultureSpecific]
 [Display(GroupName = LocalGroupNames.Tab1, Name = "Tab 1 Title", Order = 200)]
 public virtual String Tab1Title { get; set; }

Then voila you have a nicely grouped set of tabs in Episerver!

sorted tabs in episerver

Episerver: How to upload media assets in bulk

Today I created a small tool in Episerver that reads through my local file system and programmatically uploads all media files and folders in the same structure as they are in the file system. As of this writing, Episerver does not currently support uploading of folders and subdirectories.

I have developed the tool to show within the Episerver > CMS > Admin > Tools section and looks like this

Import media assets form

The destination field lets users choose to upload to either the GlobalAssetFolder or to a SiteAssetsFolder.

When it finishes the upload, the file system that looks like this:

import media assets directory

Will then be transferred including all subdirectories and files to the media asset pane:

import media asset pane

So how did I achieve this? It’s pretty simple!

First of all, update your web.config to let your application know that you have a publicmodule.

<episerver.shell>
   <publicModules rootPath="~/modules/" autoDiscovery="Modules">
      <add name="ImportMediaAssets" />
   </publicModules>
 </episerver.shell>

Then I created the following controller under inside the modules folder.

[GuiPlugIn(
 Area = PlugInArea.AdminMenu,
 Url = "/ImportMediaAssets",
 DisplayName = "Import media assets")]
 public class ImportMediaAssetsController : Controller
 {
    // Insert your logic here...
 }

Notes regarding the above code:

  • GuiPlugin – attribute that allows you to extend the Episerver user-interface.
  • GuiPlugin Area – tells the Episerver user-interface where to put your plugin
  • GuiPlugin Url – If you’re using MVC, then this will be the controller name + action name (empty if Index)

Then, in my controller, I have two action methods. The Index() gets called on click of the “Import media assets” link in the Tools menu. While the BeginUpload() gets called on submit of the form, just like hooking up any other MVC post.

public ActionResult Index()
{
   var model = new ImportMediaAssetModel();
   return View(model);
}

public ActionResult BeginUpload(ImportMediaAssetModel postedModel)
{
   var directory = new DirectoryInfo(postedModel.SourcePath);

   if (postedModel.Destination == ImportMediaAssetModel.GlobalAssetFolder)
   {
      CreateAndUploadFilesAndFolders(directory, ContentReference.SiteBlockFolder, postedModel.OverrideExistingItems);
   }
   return View(directory);
}

The important function here is the CreateUploadFilesAndFolders() method.

  • Iterate through each subdirectory and programmatically create in Episerver
  • Iterate through each file and call the UploadFile() method which we will look at later
private void CreateAndUploadFilesAndFolders(DirectoryInfo dir, ContentReference parentLink, bool overrideFiles)
{
   foreach (var childFile in dir.GetFiles())
   {
      if (fileCount >= 1) break;
      UploadFile(childFile, parentLink, overrideFiles);
   }
   foreach (var childDir in dir.GetDirectories())
   {
      var storedFolder = _contentRepository.Service.GetBySegment(parentLink, childDir.Name,
 LanguageSelector.AutoDetect()) as ContentFolder;

      if (storedFolder != null)
      {
         CreateAndUploadFilesAndFolders(childDir, storedFolder.ContentLink, overrideFiles);
      }
      else
      {
         var contentFolder = _contentRepository.Service.GetDefault<ContentAssetFolder>(parentLink);
         contentFolder.Name = childDir.Name;
         var contentAssetsFolderReference = _contentRepository.Service.Save(contentFolder, SaveAction.Publish);
         CreateAndUploadFilesAndFolders(childDir, contentAssetsFolderReference, overrideFiles);
       }
    }
}

Finally, here’s my UploadFile() implementation.

private void UploadFile(FileInfo file, ContentReference parentLink, bool overrideFiles)
{
   //Get a suitable MediaData type from extension
   var extension = GetExtension(file.Name);
   var mediaType = _contentMediaResolver.Service.GetFirstMatching(extension);
   var contentType = _contentTypeRepository.Service.Load(mediaType);

   // Try get existing media
   var storedMedia = _contentRepository.Service.GetBySegment(parentLink, file.Name,
 LanguageSelector.AutoDetect()) as IContentMedia;
   if (!overrideFiles && storedMedia != null) return;

   //Get a new empty file data
   var media = _contentRepository.Service.GetDefault<IContentMedia>(parentLink, contentType.ID);
   var container = media.BinaryDataContainer;
   var blob = _blobFactory.Service.CreateBlob(container, extension);
   using (var fs = new FileStream(file.FullName, FileMode.Open))
   {
      blob.Write(fs);
   }
   media.BinaryData = blob;
   media.Name = file.Name;
   _contentRepository.Service.Save(media, SaveAction.Publish, AccessLevel.NoAccess);
}

Of course you have to also implement the razor view, that will be completely up to you. But my model looks like this:

public class ImportMediaAssetModel
{
   public string Destination { get; set; }
   public string SourcePath { get; set; }
   public bool OverrideExistingItems { get; set; }
}

Easy right? 🙂

If you have any questions, please let me know!