Using PHPList to send bulk emails for newsletter

Instead of developing a newsletter system, I thought it would be easier to find an already made solution which I could use with minimal effort. PHPList was the first when it comes to this sort of functionality. However I was not very much pleased to have all the emails I send with an image of phplist at the bottom, not because I did not want to give credit to the developers but only because I thought it would be considered paid advertising in my newsletter.

I thought that I needed a very basic system in place. I already have the email address of my subscribers, I just need to mass email them and give them the option to unsubscribe as well. It sounds easy but then again, I didn’t want a rushed solution, so I decided to go ahead with PHPList but opting for a text link instead of the image credit to the author.

I installed PHPList through Fantastico on Linux CPanel and I’m sure you can install it with Softaculous as well. When the installation was complete, I quickly navigated to the interface but was met with a deadend. These are the problems I encountered:

500 internal server error

To fix this problem, I had to remove the line for php magic quotes from the .htaccess file. You can uncomment it as well by placing the hash symbol (#) at the beginning of the line as follows:

#php_flag magic_quotes_gpc on

Could not load xml for FCK Editor

When going into messages to compose a newsletter, I couldn’t type anything into the rich text box (FCK Editor) because there was a problem with loading the xml for that file. I had to modify the htaccess file to include the fckstyles.xml file in the allowed list as follows:

<FilesMatch “(index.php|dl.php|ut.php|lt.php|download.php|fckstyles.xml)$”>
Order allow,deny
allow from all
</FilesMatch>

Users are set to receive TEXT instead of HTML by default when importing

If the users in your lists are set to receive text, your html newsletter will be converted to text when sent to them. This is really a bad thing especially if you know the users can receive html in their email. You can go to your main page on phplist where you’ll find the option to reconcile users and from there, you can “mark all users to receive html”. You can also add a column to your file (CSV) with the name “Send this user HTML emails” and a value of 1 so that all imported users are default to receive html emails.

Cron jobs not working

With the command line cron job, the queue was not being processed because a login username and password was required. So I was getting emails to enter the login in. Mind you that the emails were text based and I had to copy the source to a new file, save as html, to view it properly. Anyway, I fixed that problem by using cURL and providing the username and password as follows:

curl ‘http://www.domain.com/phplist/admin/index.php?page=processqueue&login=username&password=password’

My cron job runs every 10 mins as I have defined a batch mode of 20 emails every 10 mins because I’m on a shared server which only allows 150 emails per hour. I decided to go safe with 120 per hour.

And to get the cron job to not email me every time it runs, I had to change the output to this:

curl ‘http://www.domain.com/phplist/admin/index.php?page=processqueue&login=username&password=password’ > /dev/null 2>&1

The 2>&1 is required because it tells linux not to print any error messages as well (so no mailing of any sort to you).

Number 0 is for standard input

Number 1 for standard output

Number 2 for errors

Anyway, so far so good. Let’s if the newsletter brings in more traffic!

How to map another file extension to the php parser through htaccess

I was in the process of cleaning up my web hosting accounts when I came across one plan which had 2 sites on it. One of them was a static website (pure html) and the other one was written in classic asp. So I decided to move both sites to another hosting plan because it was just a waste of money as the sites did not have much traffic and there was no justification why the hosting was needed.

Since windows hosting plans are more expensive, I wanted to move the sites to a linux server and I knew I wouldn’t have any problems with the html website but I wondered how much trouble it would be to move the classic asp website. The code for the dynamic website was not using a lot of asp code as in only the files ended with an .asp extension but a php parser could very well serve the webpage. So after much googling, I found that adding a line to the .htaccess file would map any file extension to the php and that would do the trick for me.

Mapping asp to php parser

The line below will tell the php parser to treat files ending with .asp extension as if they were a php file.

AddType application/x-httpd-php .asp

How the above line did not work for me and I had a lot of problems trying to map the classic asp code before I found the solution. It has something to do with the version of php you’re using. Mine was 5.12.53 I think. I added the following lines in my .htaccess file and it worked:

AddType application/x-httpd-php4 .asp
AddType application/x-httpd-php5 .asp

Both lines are required. There’s something else that you need to do if you can’t get it to work. You can replace AddType with AddHandler (that depends if you’re running Apache as a module or CGI script). And sometimes you might need to remove the application word and have only this:

AddHandler x-httpd-php5 .asp

If you wanted to map another extension, you would do this:

AddType application/x-httpd-php5 .html .htm .me .newextension

Files ending with .html, .htm, .me and .newextension will then be run through the php parser and it would allow you to serve dynamic content through .html files or hide (disguise) the programming language you’re using on your website or make it easy to move from one coding language to another because if you don’t use a file extension reserved for a particular programming language, then you can easily switch to another programming language or server.

While mapping another extension, I also did two other things:

Define a default document (so that index.asp would be recognised as a default webpage for the folder/directory) -

DirectoryIndex index.html index.htm index.php index.asp

Disable directory browsing (so that folders/directories without a default webpage are not listed) -

Options -Indexes

Well so far so good, now just waiting for DNS propagation to complete before I can cancel the hosting plan.

 

Fixing the problem with Static Compression Module frequentHitThreshold

While trying to speed up my website, I struggled with getting the StaticCompressionModule to work on my local machine. By default, the static compression module is enabled so you would think that all your static files like javascript and css will be gzipped before being sent to your browser. However this is not necessarily the case as I found out with Chrome Developer Tools and the Firefox YSlow plugin.

Although the module is enabled, static files are only going to be compressed if they meet a certain criteria determined by the frequency of access controlled by two parameters (frequentHitThreshold and frequentHitTimePeriod). The first parameter is set to 2 and the second one to 10 seconds by default. This means that if a period of 10s, if you get 2 request for your static files then they will be sent as gzip. However this is not what I wanted and I needed to correct this behaviour.

It is possible to change the values for these parameters but when I put it in the web.config file, I got the following error:

This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault=”Deny”), or set explicitly by a location tag with overrideMode=”Deny” or the legacy allowOverride=”false”.

I googled the problem and thought I’d give the command line a try:

%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/serverRuntime -frequentHitThreshold:1

Substitute your windir accordingly.

That worked great but I wanted to know why the web.config gave an error and it turned out that the config needed to be within a location tag  as follows:


<location path="Default Website">
 <system.webServer>
 <serverRuntime enabled="true" frequentHitThreshold="1" frequentHitTimePeriod="00:00:10" />
 </system.webServer>
 </location>

Once the value of the frequentHitThreshold was changed to 1, I was able to see the content compressed before being sent out from the server to the browser. Sweet!

Output caching in ASP.NET with text substitution and QueryString criteria

To speed up a dynamic website that I maintain, I decided to use the output caching mechanism built into ASP.NET. I wanted to bypass the page lifecycle and database roundtrips to achieve a faster loading webpage. However I was faced with a couple problems which are worth noting.

Memory is more expensive than disk space so it was important that I cache the webpages to disk rather than in-memory. I therefore hook up a custom output cache provider by leveraging the features of ASP.NET 4.0 and tailored it for disk caching. This worked fine for the caching portion but I also needed to evict certain pages based on user actions.

Deleting output cache for a single page based on QueryString

Delete the cache version of a page like “Product.aspx” is easy but if you wanted to evict “Product.aspx?id=4″ from the cache, then there’s no easy solution. There is only one method to delete cache : HttpResponse.RemoveOutputCacheItem which takes a virtual absolute path to work. When I looked at the code through reflection, I saw that this method was taking in parameters like the path to the webpage, the HttpVerb (GET or POST), the VaryBy parameters to generate a cache key for that particular webpage. To evict an item from the cache programmatically, you would need to know the cache key and it’s actually difficult to recompute the key without making your own method which will be an overkill.

To overcome the problem, I changed the custom cache provider and had a static RemoveItemFromCache method which takes in a cached key and deletes the file. I analysed the cached key that was being generated (eg a2/product.aspxHQNidV4FCDE) and realised that after HQN it was the QueryString parameter that was inserted and before FCDE it was the value of the QueryString. I could therefore delete a particular page by entering the cache key. Note that when the cached files are saved to disk, the “/” in the file path are replaced with an underscore “_”.

Session is not accessible when output caching is enabled

Full page caching is better than caching multiple parts of the webpage through user controls as you will see a better performance with the former. However I needed to have login links for anonymous users (users which are not logged in yet) and logout links for those who are currently signed in. There’s a substitution control which you can use for that matter :

<asp:Substitution ID=”MySubstitutionControl” MethodName=”" runat=”server” />

The MethodName that you specify will return a string which will be inserted in the cached page. This is known as donut caching as you’re caching the whole page but have holes in it (placeholders) where you can insert dynamic text.  You can also use Response.WriteSubstitution in your code behind if you don’t like the idea of declarative syntax.

The problem with the substitution control however is that the Session object is not accessible. I was storing the username of the currently logged in user in the session but with the output caching, I was not able to retrieve this info from the session as it was always null. It makes sense though because when using output caching you’re bypassing the asp.net page life cycle and the session object is not populated and the page is rather retrieved from cache. For me, it was important to display the username with a logout link though and I had to find a solution to it.

My workaround was to use a cookie to store the username when people sign in. Since the cookies collection is available (as well as the request object), I was able to display the logout link with the username properly.

AdRotator does not do post cache substitution when the AdCreated method has been overridden

The ASP.NET team says that the AdRotator control has built in post cache substitution and while this is true, you will encounter problems if you override the AdCreated Event. By doing this, you’re losing the post cache features and you’ll notice that an advertisement banner that was shown on the first view of a page before it is cached will be shown on subsequent views of that page because substitution will not occur. To fix the problem, you’ll have to come with your own post caching mechanism for this control or build your own custom control to display dynamic banners which is not a tough task.

Server.Transfer will not cache out page response

On one particular page, I was using a Default.aspx page in the root to transfer to another page where the logic is run. This is because I wanted a particular folder to display the same content (varied by querystring) from a different page. The server.transfer method worked fine except when I enabled caching on the transferred to page. So if Page A is transferred to Page B and Page B has the output cache, you’ll find that Page A won’t be output cached.

The solution to this problem is to use Server.Execute instead of Server.Transfer and Page A will be cached.

Server.Execute(“~/MyPageA.aspx”, Response.Output);

Programmatically using output cache in code behind

I found that using Response.Cache with multiple VaryByParam parameters does not work well. So if you use this:


Response.Cache.SetExpires(DateTime.Now.AddSeconds(36000));
Response.Cache.SetCacheability(HttpCacheability.Server);
Response.Cache.VaryByParams["p"] = true;
Response.Cache.VaryByParams["s"] = true;
Response.Cache.VaryByParams["t"] = true;
Response.Cache.SetVaryByCustom("RawUrl");
Response.Cache.SetValidUntilExpires(true);

You’ll have to use this instead :

Response.Cache.SetExpires(DateTime.Now.AddSeconds(36000));
Response.Cache.SetCacheability(HttpCacheability.Server);
Response.Cache.VaryByParams["*"] = true;
Response.Cache.SetVaryByCustom("RawUrl");
Response.Cache.SetValidUntilExpires(true);

The wildcard (*) will cache your pages properly.

Google Chrome malware warning

The day before yesterday, I went to check my emails on Hotmail and to my surprise I saw a big malware warning from Google Chrome telling me that a particular domain is known to distribute malware and I should really not proceed with my request. I was shocked because Hotmail is a big company and a trusted site and I don’t think it would be possible to hack into the website to make it distribute malware. I googled the problem and could not find anything on the topic. I went onto Twitter as well just to make sure that it was not something that Google has not picked up yet but social networking site Twitter has because it’s more real time. Unfortunately there was not a single clue.

However the domain that was flagged as malware seemed familar but I could not remember what exactly though. After some time digging into my rusty brain, I finally realised that this domain (completely unassociated with Hotmail) was the status message of someone I know on MSN Messenger. It was someone who was into Graphic Design that I added to my contacts. It then occurred to me that his site was compromised and because his website link is in my contacts list on Hotmail, Google Chrome was warning me of the potential danger of clicking through this link. It was not very obvious though and it appeared that Hotmail was the one which was hacked into.

After removing this person from my contact list in Hotmail, the malware message no longer appeared when I checked my emails. So if you see this kind of message when browsing a trusted site, then it could be because one of the websites links on that website has been flagged as malware.

Configuring Windows Firewall to use IP range to restrict access to your server

To keep your Windows 2008 server secure, it is important to restrict access to who is allowed to connect to your server. You may have programs like FileZilla, SQL Server/MySql and Remote Desktop and just having a strong password for those services is not enough. You should therefore create Windows Firewall inbound rules to prevent unauthorised access to your server.

My previous ISP gave me an IP address which lasted at least 6 months before being renewed. So I could add my IP address to the Windows Firewall and deny access to all others. That worked fine until I changed ISP and everytime I connect I was given a new IP address. This caused a huge problem for me and I needed to find a solution. I didn’t want to remove that Windows Firewall rule and just have the password as a method of defence against hackers or opportunists.

So I started analysing the IP addresses that were assigned to me by my new ISP every day or so. From the data I gathered, I found a pattern which shows that the first 2 digits were fixed, the 3rd one varied between just two consecutive numbers and the fourth one was dynamic (as in it could be any number). With that information, I knew exactly what I had to do. Instead of listing a static IP in Windows Firewall, I could set an IP range which could connect to the server.

Let’s suppose, the lowest IP address was 140.192.45.0 and the highest was 140.192.46.255. You could put that in the Firewall inbound rule easily as the starting from and upto range as follows:

IP address range

However there’s a more elegant way of doing that – by specifying a subnet mask. You could try type in 140.192.45.0/23 as follows:

ip address subnet mask

I’ve used the subnet calculator to correctly assign the range : http://www.subnet-calculator.com/cidr.php

Note that although this configuration will allow a lot of IP addresses within that range to access the server, it’s better than leaving it open to the whole world. The combination of the IP range along with a strong password makes it more secure than having no IP restriction at all.

5 months since I last blogged

It’s been a really long while since I last blogged (5 months). There’s been some major changes in my life and I didn’t have the time for writing although I wish I had. My main reason for blogging is to write on the problems that I encounter while programming so that people who find themselves in similar situations can find the solutions. However it takes time to write up a good post and although there’s nothing wrong in writing a short blog post, I believe a good write-up should be complete in itself and not lacking in content.

Sometimes it takes hours or days to find solutions and by the time I get something to work, it’s just too much work to post the solution online. The thing is that we all have busy lives and I’m not an exception to that.

How to do a one-to-one mapping in Fluent nHibernate

In most circumstances, you will use a many-to-one mapping and that’s easily created with fluent nHibernate using the “References” relationship. However if you need to perform a 1 to 1 mapping, then things get tricky. Say for example, you have a user record and wanted to have the user preferences in another table. Each user will have a record in the preferences table so it’s a direct one to one mapping. You will then have to use the HasOne relationship in fluent nhibernate.

public class User
{
    public virtual int Id { get; set; }
    public virtual string Username { get; set; }
    public virtual UserPreference Preference { get; set; }
}

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id);
        Map(x => x.Username);

        HasOne(x => x.Preference);
    }
}

public class UserPreference
{
    public virtual int UserId { get; set; }
    public virtual bool Newsletter { get; set; }
}

public class UserPreferenceMap : ClassMap<UserPreference>
{
    public UserPreferenceMap()
    {
        Id(x => x.UserId).GeneratedBy.Assigned();
        Map(x => x.Newsletter);
    }
}

So we have a User table with Id set as the primary key and we have a second table UserPreference with UserId as the primary key. The Id on the user table is autogenerated by the database but when a user entity has already been created, we pass this object to the UserPreference entity so that it can get the UserId. That’s why we need to specify in the mapping class for UserPreferenceMap that the Id is Assigned.

Google changing page titles in SERPs

While searching where one website was ranked for in Google for a search term, I noticed something really strange in the SERPs. Google has changed the title of the webpage by appending the site’s name to the end of the title. To give you an idea of what’s happening, consider the following:

Webpage title : Basic SEO Skills
Website name : My Website

You would think that “Basic seo skills” would be listed as the title in google search result pages but no, the title was displayed as “Basic SEO Skills – My Website”, so the suffix “My website” was appended to the real title. I checked the said webpage and the website name was not in the title which means Google was deliberately adding this to the title. Funny thing is that for a different search term (variation of the main keywords), the right title is shown.

So I did a little research and it turns out that Google reserves the right to alter your page title tag to show a more relevant result to the searcher (this depends on what search terms are being used). I knew they showed a more relevant snippet (text from your webpage where the search terms are found) instead of the description tag but this title thing was new to me.

Where does Google pull the additional words to append to your title tag?
Well it turns out that the suffix can come from anchor text pointing to your page, your website name or from text in your content.

When does Google alter your page title?
Seems like whenever your webpage has been deemed a good result to display for a particular search term but your title does not contain an essential keyword to make the search result more appealing, Google will spice up your title by appending the search term from, say, the inbound link’s anchor text.

If your page title is short or not descriptive as well (eg Untitled or no title at all), Google will most likely change it to better reflect your content.

Enum conversion in C#

Just for reference, here are some examples on how enums can be converted in C#:

public class MyAnimalClass
{
    public enum Animal
    {
        Cat = 1,
        Dog = 2,
        Bird = 3
    }

    // enum to string
    public void Hey()
    {
        Animal animal = Animal.Bird;
        string animalText = animal.ToString(); // Bird

        HttpContext.Current.Response.Write("<br />enum to string : " + animalText);
    }

    // string to enum
    public void Hey2()
    {
        string animalText = "cat";
        Animal animal;
        // Enum.TryParse will return false if the string value cannot be converted to the enum type
        if (!Enum.TryParse<Animal>(animalText, out animal))
            HttpContext.Current.Response.Write("<br />string to enum is null for : " + animalText);
        else
            HttpContext.Current.Response.Write("<br />string to enum: " + animal.ToString()); // Animal.Cat

        Animal animal2 = (Animal)Enum.Parse(typeof(Animal), animalText, true); // Animal.Cat (case insensitive comparison)

        HttpContext.Current.Response.Write("<br />string to enum insensitive: " + animal2.ToString());
    }

    // int to enum
    public void Hey3()
    {
        int animalInt = 2;
        Animal animal = (Animal)animalInt;

        HttpContext.Current.Response.Write("<br />int to enum : " + animal.ToString());
    }

    // enum to int
    public void Hey4()
    {
        Animal animal = Animal.Bird;
        int animalInt = (int)animal;

        HttpContext.Current.Response.Write("<br />enum to int : " + animalInt.ToString());
    }
}

Using enums when coding makes life easier as you can easily tell what sort of object you’re dealing with rather than having to guess what the numbers (1,2,3, etc) mean really.