Skip Ribbon Commands
Skip to main content

Quick Launch

Home
July 26
Managed Metadata Field Types and InfoPath List Forms - SharePoint 2010

I came across a minor limitation in 2010 today that is worth keeping in the back of your mind.

 

Scenario

You create a SharePoint list that has a column type of 'Managed Metadata'.

You then decide you want to customise the SharePoint list form using InfoPath.

 

 

Problem

When opening the list form in InfoPath you receive the following warning:

 

On opening the form in design mode, the 'Managed Metadata' field is not available on the form canvas and cannot be added

 

On publishing the form and then creating a new item in the list the managed metadata field is not available to complete

 

If the 'Managed Metadata' field is mandatory – on creating a new item in the list you receive the error "The following required fields are missing from the form".

 

Moral of the story – Managed Metadata field types and InfoPath forms don't play nicely together

March 23
Australia and New Zealand SharePoint 2010 Conference

Registration is now open for the SharePoint 2010 Conference in Australia and New Zealand.

 

Location: Hilton Sydney, Australia
Dates: 16th & 17th June 2010
http://www.sharepointconference.com.au/


 

 

Location: Duxton Hotel, Wellington
Dates: 9th & 10th June 2010
http://www.sharepointconference.co.nz/

September 17
Use the Contact Selector in InfoPath to get user details – no code required

Ah, my love affair with the contact selector continues J. Ever wanted to pull a specific user's details into your InfoPath form? It's pretty easy to get the current user's details. But try to get another user's details and it becomes pretty difficult. All of the solutions I have read up until now have involved code (and I don't like to code).

Well, recently I was doing some reading and I stumbled across this great blog by Clayton Cobb. It shows how you can get any details (department, office, manager etc) for any user you like and pull the data into an InfoPath form. How awesome!! I thought I'd extend Clayton's idea and incorporate the contact selector into the mix.

 

To get the most out of this post, you need to understand a couple of key concepts:

  1. How to set up a contact selector in an InfoPath form. Check this blog out if you need help.
  2. How to use the UserProfileService. Itay Shakury's blog can help you with this.

I would suggest reading and doing the steps in these blogs first before you attempt to go any further.

 

Ok, here we go.

I'm assuming that you have read the above 2 blogs and therefore should have the following already set up in your InfoPath form:

  • The contact selector
  • A data connection set up to the 'GetUserProfileByName' method

 

For this post, I'm going to get the user's manager and the user's department. I have set up my form so it looks like this:

 

And the main data source looks like this:

 

 

GET THE EMPLOYEE'S USER NAME WITHOUT THE DOMAIN

We are going to be passing the employee's user name into the 'GetUserProfileByName' method to extract the relevant department and manager. For this to work we need to extract the employee's user name, without the domain, from the 'AccountId' field in the contact selector.

Double click on the 'EmployeeUserName' field to open the field's properties

 

Click on 'fx' next to the 'Value' field.

Insert function

Select the 'substring-after' function

Set up your formula to look like this:

 

This is going to take everything after the "\" in the account ID field. So for example, if I enter my name in the contact selector the account ID will look something like this "MYDOMAIN\ahelbig". By using the substring-after value we are left with 'ahelbig'.

Click Ok and the Ok again.

 

Try previewing your form at this point and enter a name in the contact selector. You should see something similar to this:

 

 

GET THE DEPARTMENT

 

Now we can get into the fun stuff..... We will begin by extracting the department of the employee. All I'm doing here is using the steps in Clayton's blog to achieve this. Clayton has done such a great job of describing the why's so I'm not going to do that here. If you need more information, jump over into Clayton's blog.

  1. First we create a rule that sets the 'AccountName' in the 'GetUserProfileByName' data source.

    Double click on the 'EmployeeUserName' field in the main data source to open the field's properties

     

    Click on 'Rules and Merge' and then click 'Add'

    Give the rule a name such as 'Get Employee Info'

    No condition is required

    Click 'Add Action'

    Leave the action as 'Set a field's value'

    Click on the button next to the 'field' field

    In the data source drop down select the GetUserProfileByName Data source

    Expand out the groups and select the 'AccountName' field

    Click on Ok

    Click the 'fx' button next to the 'Value' field

    Click on 'Insert field or group' and then select the 'EmployeeUserName' field

    Click on ok and then ok again

    Your action should look like this:

    Click on Ok

     

  2. Back in the 'Rules' window click on 'Add Action' again.

    In the Action drop down box, select 'Query using a data connection'

    In the 'data connection' field select the 'GetUserProfileByName' data source

    It should look like this:

     

    Click on Ok to save the action

     

  3. Back in the 'Rules' window, we are going to add one more action. So, click on 'Add Action' again

    Leave the action as 'Set a field's value'

    Click on the button next to the 'field' field and select the 'Department' field

    Click on Ok

    Click on 'fx' next to the 'Value' field. Now this is where understanding Itay Shakury's blog comes in handy.

    Insert a field or group

    Click on the data source drop down and select the 'GetUserProfileByName' data source

    Expand out the groups and select the 'Value' field.

    Click on 'Filter Data'

    Click on 'Add'

    In the first drop down box select 'Select a field or group'

    Select the 'Name' field from the 'GetUserProfileByName' data source

    Click on Ok

    Leave the condition as 'Is equal to'

    In the last drop down box enter 'Department'

    Your filter condition should look like this:

    Click on Ok until you are back at the 'action' window. Your action should look like this:

    Click on Ok to save the action

    You should have 3 actions that look like this:

    Note: It's very important that the actions appear in the order as shown

    Click ok 3 times to save the rule.

    Now is a good time to preview or publish your form and check that everything is working. Go ahead and enter a name into the Contact Selector and see the 'Department' field auto populate.

    If you're anything like me, it was about this point in the process that I started to get really excited....

     

     

GET THE MANAGER'S NAME

If you have managed to get this far and everything is working ok, then these next few steps will be pretty straight forward. Basically, we are repeating what we have already done for department except this time we are going to get the manager.

 

    Open the 'Get Employee Info' rule you created on the 'EmployeeUserName' field

Click on 'Add Action' again

Leave the action as 'Set a field's value'

Click on the button next to the 'field' field and select the 'Manager' field

Click on Ok

Click on 'fx' next to the 'Value' field.

Insert a field or group

Click on the data source drop down and select the 'GetUserProfileByName' data source

Expand out the groups and select the 'Value' field.

Click on 'Filter Data'

Click on 'Add'

In the first drop down box select 'Select a field or group'

Select the 'Name' field from the 'GetUserProfileByName' data source

Click on Ok

Leave the condition as 'Is equal to'

In the last drop down box enter 'Manager'

Your filter condition should look like this:

Click on Ok until you are back at the 'action' window. Your action should look like this:

Click on Ok to save the action.
You should have 4 actions that look like this:

Note: Once again, it's very important that the first 2 actions appear in order as shown - the other actions can be in any order

    Click ok 2 times to save the rule

    Test the form to make sure that the manager's user name is populating.

    

 

Now at this point, you really are done. Most people just want to know the manager for the purposes of some kind of workflow. For example, you may want to route this form to the employee's manager for approval. Technically, you have the manager's user name, which is enough and can be used to assign tasks and send emails to the manager using SharePoint Designer workflow or some other workflow tool. Personally, if my client wants the manager's name to be displayed on the form, or perhaps in email notifications, I don't like just having the manager's user name. I think it looks ugly. I would much rather have the manager's full name displayed nicely on the form and just use the manager's user name for the purpose of sending email notifications with workflow. Clayton's post shows very nicely how to achieve this by using the 'PreferredName' so definitely worth a look if you haven't already.

 

I know this was a long post but hopefully you have the idea. I've only shown you how to extract 2 pieces of information about the employee. Of course, you can use this method to extract any other bits of information as you need. Refer to the long list of user information that Itay Shakury's provides in his blog.

 

Some final notes:

  • You might also like to consider limiting the contact selector so that only one name can be entered. If you think this is necessary, click here to find out how.
  • Once again, special thanks to Clayton, who totally made my day when I read his blog.

 

Stay tuned for my final post which talks you through how to validate the name in the contact selector.

July 31
Validate the Contact Selector in InfoPath – Make the Contact Selector Mandatory (version 2)

Here is a very quick and simple method of making your contact selector in your InfoPath form mandatory.

There are plenty of blogs out there that describe how to set up the contact selector in your InfoPath form so I won't bother documenting that here. If you need help, check out this blog.

After you have added your contact selector to the form, it will most likely look something like this:

 

Your data source will look similar to this

Note: Check to make sure your contact selector is working correctly before continuing with this blog.

 

  1. Firstly, add a new field to your data source called 'EmployeeName'. This will be the field that performs the validation check.

     

    To add a new field, click on the "myFields" group in your datasource

     

    Then click on 'Add new Field or Group'

     

    Enter a name for your field and then click on Ok when you are done

     

    After you have finished adding the field, your data source should look similar to this:

     

     

  2. Next we are going to set the 'EmployeeName' field to equal the display name entered in the contact selector.

    Double click on the EmployeeName field to open the properties

    Set the default value of the field to 'DisplayName'

     

     

  3. Now that the 'EmployeeName' equals the DisplayName all we need to do is set a validation rule on the EmployeeName so that it can never be blank.

    Click on the 'Validation' tab in the EmployeeName field properties

    Click on Add. Then set your validation rule as shown below.

    Click Ok and Ok again to save the changes

     

    I always put any validation rules for the contact selector on the 'EmployeeName' field rather than on the Contact Selector itself. My reasoning behind this relates to the validation error message that the user sees (below). Notice that it includes the name of the field that holds the error. 'Employee Name' makes it easy for the user to identify the field with the error. If you place the validation condition on one of the Contact Selector fields such as 'DisplayName' it is not very intuitive.

    

 

  1. Another thing that you can do to notify your users of the validation error is to include an error warning next to the contact selector on the form canvas.

    On your form canvas, insert a section and in the section enter "Cannot be blank"

    You might like to highlight the text in red and make it italics so it stands out.

     

  2. Double click on the section you added to open the group properties

    Click on the Display tab and then click 'Conditional Formatting'

    Click on 'Add'

    Set the condition "EmployeeName is not blank"

    Tick the 'hide this control' checkbox

    The condition should look like this:

     

    Click on Ok 3 times to save the changes

     

  3. Publish your form.

    Open a new form and you should see the error warning next to the contact selector name. If you enter a name into the contact selector, then the error message should disappear.

 

If you found this post useful, you may also find my post on limiting the number of names in the contact selector useful too.

July 18
Use the Contact Selector in InfoPath to get user details – no code required
July 17
Validate the Contact Selector in InfoPath – Make the Contact Selector Mandatory

After I published this post, I had a couple of people comment that this method doesn't work as well as it should. While this method works fine in browser enabled forms, in the client, it is not so great. Here is a newer post I have just done which is a much simpler method for making your contact selector mandatory and works perfectly in both the browser and the client. Thanks to everyone who posted your useful comments… I really do appreciate your feedback.

 

Before reading this blog you may want to take a look at

 

Have you ever tried to make the contact selector mandatory? I don't know about you, but I initially had some troubles with it. I tried making the 'DisplayName', 'AccountId' and 'AccountType' fields mandatory. I tried adding some validation to the same fields. Neither of these ideas worked. Then I realised, if I could somehow determine how many names have been entered in the contact selector, I should be able to add some validation conditions to ensure the field is never blank.

This concept is exactly the same as the method I used in my first post, just with slightly different validation conditions.

Follow the first 3 steps in my initial blog then:

 

  1. Double click on the 'EmployeeName' field in the data source to open the field's properties.

     

    Click on the validation tab and then click 'Add'

    Set the condition "EmployeeNameCount is less than 1"

    In the 'Screen Tip' field enter "Cannot be blank"

    Your validation condition should look like this:

     

    Click on Ok to save the validation and then Ok again to save the changes to the field's properties.

     

     

  2. Publish your form, don't enter any names into the contact selector and then submit.

    If you are using a browser enabled form, the validation error message you get should look similar to this:

     

     

  3. As mentioned in my last blog, it's a good idea to include an error warning next to the contact selector on the form canvas, especially if you are using a client based form.

    On your form canvas, insert a section and in the section enter "* Cannot be blank"

    You might like to highlight the text in red and make it italics so it stands out.

     

  4. Double click on the section you added to open the group properties

    Click on the Display tab and then click 'Conditional Formatting'

    Click on 'Add'

    Set the condition "EmployeeNameCount is greater than or equal to 1"

    Tick the 'hide this control' checkbox

    The condition should look like this:

     

    Click on Ok 3 times to save the changes

     

  5. Publish your form.

    Open a new form and you should see the error warning next to the contact selector name. If you enter a name into the contact selector, then the error message should disappear.

     

Pretty straight forward hey?

In my next post find out how you can obtain other details, such as department, office and manager, from the contact selector.

 

July 17
Validate the Contact Selector in InfoPath – Limit the number of names a user can enter into the Contact Selector

I have to admit (even though it sounds really geeky) to my new love affair with the contact selector in InfoPath. I've always known it was there and have used it on numerous occasions but did find it a little bit limited. Lately, I've taken the time to actually look at the control properly and I've been pleasantly surprised with what I have been able to achieve. In my next few blogs, I'm going to cover some cool things that I have discovered with the control while at the same time hopefully answering some commonly asked questions for the InfoPath users out there.

This first post tries to answer the question – 'how can I limit the number of names that can be entered into the Contact Selector in my InfoPath form?' I've seen a couple of answers, none that are simple, some that even involve code. I'm no coder (and don't particularly want to be), so here is a simple solution that utilises the 'count' function and some validation conditions… yep, that's right, no code.

There are plenty of blogs out there that describe how to set up the contact selector in your InfoPath form so I won't bother documenting that here. If you need help, check out this blog.

After you have added your contact selector to the form, it will most likely look something like this:

 

Your data source will look similar to this

 

Note: Check to make sure your contact selector is working correctly before continuing with this blog.

 

As this is an expense claim, I only want the user to be able to enter one name into the contact selector. If you play around with the control, you'll find it's not as simple as just adding validation directly to the contact selector. To do this we need to add a couple extra fields to the form's data source.

  1. Firstly, add 2 new fields to your data source:
  • 'EmployeeNameCount' - This field is going to record the number of names that have been entered in the contact selector.
  • 'EmployeeName' – This field will perform the validation check.

 

To add a new field, click on the "myFields" group in your datasource

 

Then click on 'Add new Field or Group'

 

Enter a name for your field and then click on Ok when you are done

 

After you have finished adding both fields, your data source should look similar to this:

 

 

  1. Next we need to perform a count on the 'Person' repeating group and record this number in the 'EmployeeNameCount' field.

    Double click on the 'EmployeeNameCount' field to open the field's properties

    Click on the 'fx' button to insert a function

    Insert a count function and point to the 'Person' repeating group in your data source, then click on ok. Your function should look like this:

     

    Click on Ok to save the changes you have made to the 'EmployeeNameCount' field

     

     

  2. At this point you might like to test that the EmployeeNameCount field is doing what it's supposed to do. To test, drag the 'EmployeeNameCount' field onto the form canvas.

     

    Preview your form and then enter a couple of names into the contact selector. You should notice that the number in the EmployeeNameCount field corresponds with the number of names entered in the contact selector.

     

    You can now delete the 'EmployeeNameCount' field from the form canvas (but leave the field in the form's data source)

     

  3. Now we have a field counting the number of names entered in the Contact Selector, it is easy to set up validation to stop the user from entering more than one name. I prefer to set the validation check on the 'EmployeeName' field that we created at the start of this blog versus on the contact selector field itself. There is a good reason for this which I will explain later. But firstly, let's set up the validation rule.

     

    Double click on the 'EmployeeName' field in the data source to open the field's properties.

     

    Click on the validation tab and then click 'Add'

    Set the condition "EmployeeNameCount is greater than 1"

    In the 'Screen Tip' field enter "Only 1 employee name is allowed. Please remove excess names."

    Your validation condition should look like this:

     

    Click on Ok to save the validation and then Ok again to save the changes to the field's properties.

     

     

  4. Publish your form, enter 2 names into the Contact Selector and then try to submit.

    If you are using a browser enabled form, the validation error message you get should look similar to this:

     

    Earlier on in this blog I said I prefer to put the validation on the 'EmployeeName' field rather than the Contact Selector itself. My reasoning behind this relates to the error message above. Notice that it includes the name of the field that holds the error. 'Employee Name' makes it easy for the user to identify the field with the error. If you place the validation condition on one of the Contact Selector fields such as 'DisplayName' it is not very intuitive.

     

  5. Another thing that you can do to notify your users of the validation error is to include an error warning next to the contact selector on the form canvas.

    On your form canvas, insert a section and in the section enter "* Only 1 employee name is allowed. Please remove excess names."

    You might like to highlight the text in red and make it italics so it stands out.

     

  6. Double click on the section you added to open the group properties

    Click on the Display tab and then click 'Conditional Formatting'

    Click on 'Add'

    Set the condition "EmployeeNameCount is less than or equal to 1"

    Tick the 'hide this control' checkbox

    The condition should look like this:

     

    Click on Ok 3 times to save the changes

     

  7. Publish your form, enter 2 or more names into the Contact Selector.

    Notice that when you enter more than 1 name the error message is displayed, making it easy for a user to identify the cause of the validation error.

     

    If your form is client based (vs Browser enabled) then it is essential that you put this extra error warning on the form canvas. Validation errors in a client based form do not point the user to the field that is causing the error making it kind of confusing for someone when they are filling out the form.

 

That's it - you've finished. This method can be used to achieve other things as well. Say you wanted to enforce the user to input at least 2 names into the contact selector…. As long as you have the 'EmployeeNameCount' field, all it involves is setting up the correct validation conditions. Get creative and see what you can come up with.

 

Upcoming posts:

July 05
Content Types and Templates – Things I wish someone had told me

Ever since my light bulb () moment with content types, I've spent a fair amount of time working out the best way to set the little guys up. Something that I've stumbled across again and again is the (negative) effect that templates can sometimes have on content types. Below I've listed some things that I wish I had known when I first starting creating content types.....

 

Firstly, for those of you that may be new to content types.......

A template can very easily be applied to a content type by clicking on "Advanced Settings" in the settings for the content type

 

Once the template has been applied, when a user selects the content type from the 'new' menu in a library the relevant template will open.

Alternatively, if you do not apply a template, then a blank document will open (the type of document that opens will vary depending on the settings for a particular library).

 

Tip 1: Template Format

vs

The term 'template' can be misleading. Often SharePoint site owners mistakenly think that the template applied to a content type should be in an office template format (i.e. .dotx or .dot). However I have found that using an office template document stops the document information panel from opening properly when a user selects the content type from the new menu in a library. Therefore, when preparing your templates for use with content types it's worth saving them in a standard office document format such as .doc or .docx.

 

Tip 2: Legacy Properties

Quite often template owners will copy someone else's document (i.e. using the 'Save As') and use the copy to create a template. This is very common – why not? It saves time right? There is absolutely nothing wrong with doing this as long as when preparing the template for use with content types the document is checked for legacy properties. You may like to spend some time reading my post about The Importance of Document Properties which outlines the effect legacy properties have on search results in SharePoint.

If we have a document with values in the document properties, just like the screen shot below

And we use this document as a template for our content type without first removing the properties, then whenever a user selects the content type from the 'new' menu, these properties open with the template. If the user doesn't check the properties (and often they don't), then the metadata for this type of content is incorrect and this starts to play havoc with your search results.

A very simple step to ensure that your template is free from legacy properties is to inspect the document for hidden metadata and other information. To inspect an office document:

  • Open the template and click on the office button in the top right hand corner of the screen

     

  • Select "Prepare" → " Inspect Document"

     

  • Click on "Inspect"

     

  • If document Properties are found, select to "remove all"

     

  • Your document should now be free of any properties which may affect the content type

 

Tip 3: Legacy Content Type IDs

Something I certainly wasn't aware of when I first started using content types is that whenever a document is created / uploaded into SharePoint and a content type is selected, a 'Content Type ID' is inserted into the advanced properties of that document. Just so you know I'm not lying, try adding a new document into a SharePoint library and apply a content type. Then:

  • Click on the Office Button in the top left hand of the screen

     

  • Select Prepare → Properties

 

  • In the document information panel (DIP) at the top of the document, open Advanced Properties

     

  • Select the Custom tab and you'll notice that the Content Type ID is listed here

     

This ID stays with the document. Even if you download the document onto your desktop, the ID comes with it. This is not normally a problem; in fact it is quite useful - if we move documents from one location to another, the ID helps the document to remember what type of content it is. The only time this is a problem is if we try to use a document that has a content type ID as a template.

If we happen to do this (as I have done) then the content type starts to get very confused and starts to misbehave at the library level (i.e. it affects your users). Once again, it's very simple to ensure that this type of thing doesn't happen. Just run the document inspection tool (see tip 2) on your proposed template and remove any hidden metadata before applying it to the content type.

 

Tip 4: Be careful when using the 'Enter the URL of an existing document template' option

You might have noticed in the advanced settings of a content type, that there is an option to enter the URL of an existing document template.

I was really excited about this idea when I first saw it. I thought it meant I could store the document template in a library in the template owner's site. They could then freely update the template whenever they like and this would automatically update all libraries and lists using that content type. It meant I wouldn't have to give the template owner permission to modify content types, hence giving them permission to other settings that I probably didn't want them to touch.

Unfortunately, I think this idea still needs a little work..... let me show you what I mean

The Scenario

I have created a content type called 'My Company Report' and I have applied metadata columns of:

  • Date Written
  • Department
  • Brief Description

 

The next step is to create a library somewhere in SharePoint where you will store all your templates that will be applied to the content types. In this example, I have created the library in the top level site and called it 'Templates'.

I then apply the 'My Company Report' content type to that library

 

Now I upload the 'My Company Report' template into the library and I tag it with the content type 'My Company Report'. I leave the remaining fields empty.

I then copy the URL of the My Company Report template

 

Back in the advanced settings for 'My Company Report' content type I paste the URL of the template into the relevant field and say ok.

 

So far so good…..

Now my content type, with relevant metadata and template is ready to be pushed out through my site collection. I go to a library somewhere in my site collection (in this example I'm using a library called 'reports) and apply the My Company Report content type to the library

 

Now it looks like it's ready to use…. So go ahead and click to create a new My Company Report… Looks like everything worked perfectly hey? DIP opened, the document saved into the correct library, there were no errors…. In fact, yes, this method does work perfectly… until, we add in mandatory list columns.

 

List Columns

Now, in this particular reports library, my users would like to also capture the 'Project Name' against each report when saved. They only want to do it in this library and not for any other reports saved elsewhere in the site collection. So it makes sense that we would just apply the list column to this particular library and not to the content type itself.

So I go into the library and apply a new column called 'project name' – make the column mandatory. Everything seems ok….

Now I create another new My Company Report… and looks as though everything worked ok again… however if you pay particular attention to the document information panel you'll notice that there is something missing…

The 'Project Name' field you created is not there.

Go ahead and try to save your document - you'll probably get a similar error message like the one below

 

So why does this happen? When a user creates a new document from the new menu in the 'Reports' library, the settings from the template (the one saved in your central 'templates' library) overrides the settings on the 'reports' library. Then when the user saves the document, the settings from the Report library are applied and this is what causes errors, things not to appear on the DIP when they should etc etc.

This blog is long enough so I'm not going to go into any more detail but if you have a play I'm sure you'll discover some other short comings with this method.

 

So overall, when you are preparing your templates for use with content types, make sure you check these few things:

  • Use the correct template format (i.e. .doc or docx)
  • Scan your template for any legacy properties
  • Be careful when using the 'Enter the URL of an existing document template' option

Good luck!!

July 05
KPI List not available

I had a question from a client the other day regarding creating a KPI list. The user was a site collection administrator and had been creating various KPI lists in a number of the sub sites. However, in one particular site, the option to create a KPI list was not available.

KPIs are part of the SharePoint Enterprise features therefore the enterprise feature needs to be activated in order for this list to be available. So, to make the list available, go to Site Settings > Site Features and activate the feature

 

The KPI list then becomes available.

March 13
White Paper - Plan Content Types in SharePoint

One of the most common problems in any organisation today relates to the management of large amounts of information and documentation. Information workers find it difficult and time consuming to find what they are looking for, content owners struggle to keep content up to date, version control is a nightmare and processes are inefficient.

 

I have recently finished writing a white paper that introduces the concept of content types in SharePoint and explains why an organisation may choose content types to assist with information and process management. The paper outlines key factors to consider when planning for content types, the aim being to provide the reader with a logical approach to planning content types in the SharePoint environment. Finally, the paper addresses three of the most commonly used content type settings – metadata, templates and workflow – and details how each of these settings can assist an organisation with the management of information and information-related processes.

 

I wrote this paper because too many times we see companies go down the path of migrating all their documents into SharePoint without using content types, only to have them come back later and say 'we can't find anything'. Hopefully, after reading this white paper, you will understand why I keep harping on about how important content types are.
 
Happy reading!!
 

 

1 - 10Next