Recycle bin won’t empty when using EPiServer Relate

A few days ago i found i strange behaviour on our intranet site. The waste basket didn’t empty! The scheduled job ran just fine, but the log always contained the same message: 0 content items were deleted from recycle bin

Thats a new one! So where to look? The logs are, off course, a great place to start looking for errors. The first line that cought my attention was this one: ERROR EPiServer.Util.EmptyWastebasketJob: 17.3.1 Error when trying to delete content from recycle bin

At first, i thought it was some kind of permission issue. The site runs Windows authentication, and that has some side effects, but than i found another line in the log: EPiServer.Core.TypeMismatchException: Content with id ‘<xxxxx>’ is of type ‘Castle.Proxies.FormContainerBlockProxy’ which does not inherit required type ‘EPiServer.Core.PageData’

and than another similar line: EPiServer.Core.TypeMismatchException: Content with id ‘29990’ is of type ‘EPiServer.Core.ContentFolder’ which does not inherit required type ‘EPiServer.Core.PageData’

I couldn’t believe there’s still code in EPiServer that requires PageData!! And, off couse, there wasn’t. BUT! EPiServer Relate is another story. The CmsIntegrationModule had a few fingers in that cookie jar!

To be specific, the method RemovePageEntity, which deletes corresponding page entities when a page is deleted:

        /// <summary>
        /// Deletes the correspoinding <see cref="CMSPageEntity"/> when a page is deleted
        /// </summary>
        /// <param name="e">The <see cref="EPiServer.PageEventArgs"/> instance containing the event data.</param>
        private static void RemovePageEntity(PageEventArgs e)
        {
            if (e == null || PageReference.IsNullOrEmpty(e.PageLink))
            {
                return;
            }
            
            CMSPageEntity pagedataEntity = CMSPageEntityHandler.Instance.GetEntity(e.Page.PageGuid, e.Page.LanguageBranch, 0);

            if (pagedataEntity == null)
            {
                return;
            }

            CMSPageEntityHandler.Instance.RemoveEntity(pagedataEntity);
        }

It first, it looks like the code really do try to avoid null references by checking if PageEventArgs are null and than if PageEventArgs.PageLink is null, but: when you have a ContentFolder or FormContiner in the recycle bin, both of them are set. The problem is that PageEventArgs.Page is null.

So, just throw in one more null check for e.Page, like this:

        /// <summary>
        /// Deletes the correspoinding <see cref="CMSPageEntity"/> when a page is deleted
        /// </summary>
        /// <param name="e">The <see cref="EPiServer.PageEventArgs"/> instance containing the event data.</param>
        private static void RemovePageEntity(PageEventArgs e)
        {
            if (e == null || e.Page == null || PageReference.IsNullOrEmpty(e.PageLink))
            {
                return;
            }
            
            CMSPageEntity pagedataEntity = CMSPageEntityHandler.Instance.GetEntity(e.Page.PageGuid, e.Page.LanguageBranch, 0);

            if (pagedataEntity == null)
            {
                return;
            }

            CMSPageEntityHandler.Instance.RemoveEntity(pagedataEntity);
        }

And you also need to handle errors in GetClubId. I took the easy way and put a try around the code.

        private static int GetClubId(PageReference pageReference)
        {
            try
            {
                if (PageReference.IsNullOrEmpty(pageReference))
                {
                    return -1;
                }

                // Get the page
                PageData page = DataFactory.Instance.GetPage(pageReference);

                // Check if it is an article page, if not return
                if (page is ArticlePage && ((ArticlePage)page).ClubAssociation > 0)
                {
                    return ((ArticlePage)page).ClubAssociation;
                }
                return -1;
            }
            catch { }

            return -1;
        }

Now the recycle bin should empty just fine!

How i automate website creation with Powershell

If you, like me, create lots and lots of websites almost every day you probably noticed that a lot of the things you do are repetitive.

I have a lot of rules that almost all my websites follow.

  • I name the pool, the site and the folder the same (like site.domain.com)
  • I always add a binding following the pattern 17000 + site id, so a site with site id 13 has a binding for localhost:17013
  • I set idle time-out to 0
  • I add a scheduled recycle at 02:00 (for load balanced environments att set it at 03:00 for the other node)
  • I always create a scheduled job to take care of log rotation

To avoid making misstakes, and to make sure it gets done the same way every time, i use this Powershell. It’s not perfect, but it works for me, and it does exactly what i need it to.

In the end of the script, where the scheduled job is created, i use a little application called LogRotate which i may publish on the internet. In the mean time, you can use it as a way to add a scheduled task.

Maybe it will work for you as well, or maybe you can pick the parts you need. Enjoy!

#
# PowerShell Script used to create new websites. Requires Eleveted PowerShell Console!
# This script: 
# 	- Creates a new Applicatiopn Pool with our preferred settings
#	- Creates a new website using that pool
#	- Create a scheduled job to manage logfiles
#
# Make sure to edit these parameters before you run the script:
# - $AppPoolName Sets the name of AppPool and WebSite
# - $AppPoolIdentityName and $AppPoolIdentityPwd to set the identity
# - $SiteDirectory is the base path to the folder holding websites
#

#########################
# Import modules needed #
#########################

Import-Module WebAdministration

######################
# Set all parameters #
######################

#AppPool parameters

$AppPoolName 			= "Test.Domain.Com"	            #Name of ApplicationPool
$AppPoolDotNetVersion 	= "v4.0"						#.NET version for pool
$UseAppPoolIdentity     = 0                             #Set to 1 to user app-pool identity instead of domain account
$AppPoolIdentityName 	= "domain\sysAccount"			#Identity to execute pool
$AppPoolIdentityPwd 	= "P@ssw0rd"					#Identity password
$AppPoolRecycleTime		= "02:00"						#Default time is 02:00, on load balanced environments, set first host to 02:00 and second to 03:00

#WebSite parameters
$SiteName 				= $AppPoolName					#WebSite name, by default the same as name of pool
$SiteDirectory 			= "C:\WebSite\WebSite\Sites\"	#Base folder, where the site should be created. Site folder is created automatically

###########################
# Create application pool #
###########################

#Create Pool and store it in $AppPool
New-WebAppPool ñName $AppPoolName
$AppPool = Get-Item IIS:\AppPools\$($AppPoolName) 

#Stop AppPool
$AppPool | Stop-WebAppPool 

#Set additional properties
#Set AppPool Identity and password
if($UseAppPoolIdentity -eq 0)
{
    $AppPool.ProcessModel.identityType = 3
    $AppPool.ProcessModel.Username = $AppPoolIdentityName 
    $AppPool.ProcessModel.Password = $AppPoolIdentityPwd
}

#Set idle time-out to zero
$AppPool.ProcessModel.IdleTimeout = "0"

#Set desired .NET version
$AppPool.ManagedRuntimeVersion = $AppPoolDotNetVersion

#Set periodic recycle schedule
Set-ItemProperty -Path IIS:\AppPools\$($AppPoolName) -Name Recycling.periodicRestart.schedule -Value @{value=$AppPoolRecycleTime}

#Save and start AppPool
$AppPool | Set-Item
$AppPool | Start-WebAppPool

#Clear periodic restart time (defaults to 1740 minutes)
Set-ItemProperty -Path IIS:\AppPools\$($AppPoolName) -Name Recycling.periodicRestart.time -Value "00:00:00"


##################
# Create website #
##################

#Create Website Directory
New-Item -ItemType directory -Path "$($SiteDirectory)$($SiteName)"

#Create Website and set default binding
New-Website -Name $SiteName -PhysicalPath "$($SiteDirectory)$($SiteName)" -ApplicationPool $AppPoolName -Port "170$($WebSite.id)" -HostHeader "" -IPAddress "*"

#Get the newly created website and store it in $WebSite
$WebSite = Get-Item IIS:\Sites\$($SiteName) 

#Set additional binding based on AppPool name
New-WebBinding -Name $SiteName -IPAddress "*" -Port 80 -HostHeader $AppPoolName.ToLower()

#Start Website
$WebSite | Start-WebSite


######################################
# Create scheduled job for LogRotate #
# This only works on Win2012         #
######################################
#Scheduled task, LogRotate
#Theese values shouldn't be neccesarry to edit. 
#Make sure LogRotate exists under Program Files and that you WebSite-folder point to the correct location.
$TaskName 				= "LogRotate W3SVC$($WebSite.id)"
$TaskDescription 		= "Archives old logfiles for site $($SiteName)"
$TaskActionCommand		= "C:\Program Files\LogRotate\LogRotator.App.exe"
$TaskArguments			= "C:\WebSite\Logs\W3SVC$($WebSite.id) C:\WebSite\Logs\Backup W3SVC$($WebSite.id) *.log 31 true"
$TaskPath				= "LogRotate"

$PsHost = host
$OsVer = [environment]::OSVersion.Version

If(($OsVer.Major -ge 6) -and ($OsVer.Minor -ge 2) -and ($PsHost.Version.Major -ge 4))
{
    Import-Module ScheduledTasks 
	$TaskAction = New-ScheduledTaskAction -Execute $TaskActionCommand -Argument $TaskArguments
	$TaskTrigger = New-ScheduledTaskTrigger -Daily -AT "05:00"
	$Task = New-ScheduledTask -Action $TaskAction -Trigger $TaskTrigger -Description $TaskDescription
	Register-ScheduledTask $TaskName -InputObject $Task -TaskPath $TaskPath
}
Else
{
	"You need a PowerShell 4 and Windows Server 2012 to create Scheduled Task!"
	"You'll need to configure LogRotate manually."
}

 

SQL-Server service won’t start after enabling SSL

Maybe you followed this guide to enable SSL for your SQL-server instance, but when you thought you were ready, the service won’t start.

What to do next?

A good guess is that your SQL-Server error log tells you “Unable to load user-specified certificate [Cert Hash(sha1) “XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”]. The server will not accept a connection. You should verify that the certificate is correctly installed. See “Configuring Certificate for Use by SSL” in Books Online.

You can find out by logging in to SQL Server Management Studio and expand your server->Management , right-click SQL Server Logs and select View->SQL Server Log. When the Log File Viewer appears, press Filter, under Message contains text enter SSL och check the box Apply filter.

If the line above appears in your log, my guess is that your SQL-Server service account do not have the right to access the certificates keys.

And the solution?

First of all, you need to find out what account runs you SQL-Server service. It probably should be a domain account, but in my case (lab environment) i used NT Service\MSSQLSERVER.

Then, open up MMC and add the Certificate snap-in. (You probably know how, but otherwise it’s in the guide above on how to enable SSL). Locate your certificate, right-click and select All Tasks->Manage Private Keys. Probably,, your SQL-Server service account is missing in that list. If so, click add and locate your service account. If you, like me, are using NT Service\MSSQLSERVER, just select your local server as Location and paste NT Service\MSSQLSERVER into the textbox. Then click OK. In the checkboxes below the accounts, make sure to give the service account the read permission.

Now, go back to Sql Server Configuration Manager and try to start the service again. It should work now!

A10 aFlex: Temporary blacklist clients based on failed logins

I was asked if i could find a way to temporary blacklist clients after x failed logins. The reason was to avoid lockout in AD when users changed their passwords, but forgot to change the password in their phones connected through ActiveSync.

If a phone connects to ActiveSync with the wrong password, resulting in a error 401, i should be able to catch that and save the IP in a table. If it re-occurs 4 times or more in less then 10 minutes than the A10 should remember this and drop the traffic, resulting in request not being sent to the mail server and the AD account not being locked out.

Thats the theory. Below is my current solution, very much in POC state but my initial testing looks promising 🙂 But i’m sure there are many aFlex/TCL gurus out there to correct me. I’m certainly not one of them!

A few notes:

  • Because i write failed attempt to temp-table, the blacklist is 10 minutes after the last failed logins. This is probably longer than needed.
  • maxfadiledrequests is set to 5, because it always start at 2 for me.. and i’m not sure why. I did expect it to start at 1 since the initial status from server is 401, but not 2.
  • holdtime is the the in seconds the address is blocked
  • You should probably not write to log in a production environment
when RULE_INIT {
    set ::maxfailedrequests 5
    set ::holdtime 600
}

when HTTP_REQUEST {
    set key [IP::client_addr]
    if { [table lookup "blacklist" $key] != "" } {
        reject
        log "$first_key is blocked"
        return
    }

    if { [table lookup tmp_table $key] == "" } {
        table set tmp_table $key 1 indef $::holdtime
        log "$key's session table created."
        return
    }
}

when HTTP_RESPONSE {
    if { ([HTTP::status] == 401) } {
        set count [table incr tmp_table $key]
        log "failed request count: $count"
        if { $count > $::maxfailedrequests } {
            table add "blacklist" $key "blocked" indef $::holdtime
            log "$key blacklisted for $::holdtime seconds "
            table delete tmp_table $key
            reject
            return
        }
    }
}

 

Xamarin Droid: Putting LocationManager in a separate class

I almost always put all logic in som sort of separate classes in my projects. I simply think it looks better, is way more easy to understand and the better way.

When using Xamarin to create cross-plattform apps, you probably use dependency injection to minimize ugly code blocks like this.

if (Device.OS == TargetPlatform.iOS) {
}else{
}

That also means you pretty much need to create interfaces that you can use for all plattforms, which leads to this topic: You need to put logic in classes.

I had a bit of a struggle to implement location services on Android this way, hence this post. Most bloggs and articles i found used the main activity. For iOS it was very straight forward, so I’m leaving that part out.

And to be clear: This is my POC-code, not intended to copy and paste into production!

First of all, i had to create an interface i could use for iOS and Droid. I kept it simple, and only included stuff i needed: The current location, a method to initialize everything and a event to trigger when position changes.

public interface ILocationManager
{
	Tuple<double, double> GetCurrentLocation();
	void StartLocationUpdates();
	event EventHandler LocationUpdated;
}

I also created i class holding the main activity context. I needed this to be able to use the context from my class.

public class Config {
	public static MainActivity context = null;
}

In my MainActivity i set the context of the class above:

Config.context = this;

From my cross-plattform code i need to get a instance of ILocationManager for the current plattform. For this, i use the DependencyService. I Also call my StartLocationUpdates method to start getting location updates:

private ILocationManager LocManager = DependencyService.Get<ILocationManager> ();
LocManager.StartLocationUpdates ();

So far, all code except the config class is the same for both iOS and Android. But this is where i hit the wall on Android. I spent days trying to figure out how to create a class using my interface that would work in the same way my iOS code did. This is my solution

First, make sure to put this line above the namespace in your class to get dependency injection to work:

[assembly: Xamarin.Forms.Dependency (typeof(NautiCalc.Droid.LocationManager))]

The second thing i found important was to decorate your class with the Activity attribute. This makes sure your activity is inserted into the Manifest, which is important. You also need to inherit Activity class, and implement your interface.

[Activity]
public class LocationManager : Activity, NautiCalc.ILocationManager, ILocationListener

The rest is kind of straight forward. You need to implement a few methods because of the inheritance of Activity, and of course your own interface.

Here is my complete code:

using System;
using Android.Content;
using Android.App;
using Android.Locations;
using Android.OS;
using Android.Widget;

[assembly: Xamarin.Forms.Dependency (typeof(NautiCalc.Droid.LocationManager))]
namespace NautiCalc.Droid
{
	[Activity]
	public class LocationManager : Activity, NautiCalc.ILocationManager, ILocationListener
	{
		// event for the location changing
		public event EventHandler LocationUpdated = delegate {};

		protected Android.Locations.LocationManager locMgr;

		public Android.Locations.LocationManager LocMgr {
			get { return this.locMgr; }
		}

		public Tuple<double, double> GetCurrentLocation ()
		{
			Location lastKnown = locMgr.GetLastKnownLocation (Android.Locations.LocationManager.NetworkProvider);
			if (lastKnown != null) {
				return Tuple.Create (lastKnown.Latitude, lastKnown.Longitude);
			}
			return null;
		}

		public void StartLocationUpdates ()
		{
			locMgr = (Android.Locations.LocationManager)Config.context.GetSystemService (Context.LocationService);

			if (locMgr.AllProviders.Contains (Android.Locations.LocationManager.GpsProvider) && locMgr.IsProviderEnabled (Android.Locations.LocationManager.GpsProvider)) {
				locMgr.RequestLocationUpdates (Android.Locations.LocationManager.GpsProvider, 2000, 1, this);
			} 
		}

		protected override void OnPause ()
		{
			base.OnPause ();
			locMgr.RemoveUpdates (this);
		}

		public void OnProviderEnabled (string provider)
		{
		}

		public void OnProviderDisabled (string provider)
		{
		}

		public void OnStatusChanged (string provider, Availability status, Bundle extras)
		{
		}

		public void OnLocationChanged (Location location)
		{
			LocationData CurrentLocation = new LocationData ();
			CurrentLocation.Altitude = location.Altitude;
			CurrentLocation.Course = (double)location.Bearing;
			CurrentLocation.Latitude = location.Latitude;
			CurrentLocation.Longitude = location.Longitude;
			CurrentLocation.Speed = (double)location.Speed;
			LocationUpdated (this, new LocationUpdatedEventArgs (CurrentLocation));
		}
	}
}

Not that hard, once you figured it out. Happy coding!