After recently performing an upgrade of a fairly old Plone 3 site to Plone 4.1.3 my client noticed that all the icons for the default Plone content types were missing from the "Add New" drop-down menu.

After some considerable investigation I put it down to 2 things.

  1. The new Plone 4 "sunburst" theme seems to have dropped icon references in portal_types in favour of defining the icon in CSS instead.
  2. Plone 3 portal_types used an "icon" field to specify the icon image that should go with the menu item but Plone 4 uses an icon expression defined in TAL instead.

This icon expression (or icon_expr as the field is known) is blank after the Plone 3 to 4 migration. This is why there is no icon next to the content type name in the drop down menu.

I should say at this point that the theme for my migrated site is based on "Plone Classic Theme" (known as "Plone Default" in Plone 3) and this may well be why I have this missing icons problem.

It's a simple fix really:

  1. Navigate to portal_types in your plone portal.
  2. Click on "Folder" to bring up the properties for the Folder content type.
  3. Add to the icon_expr field the following TAL expression: string:${portal_url}/folder_icon.gif
  4. Repeat for the rest of the default content types using the appropriate TAL expression from the below list

    Folder
    : string:${portal_url}/folder_icon.gif
    Topic
    : string:${portal_url}/topic_icon.gif
    Event
    : string:${portal_url}/event_icon.gif
    File
    : string:${portal_url}/file_icon.gif
    Image
    : string:${portal_url}/image_icon.gif
    News
    Item: string:${portal_url}/newsitem_icon.gif
    Document
    : string:${portal_url}/document_icon.gif
    Link
    : string:${portal_url}/link_icon.gif

Or because I couldn't be bothered with such a repetitive task a wrote a script that does this for you!

Just add a new Python Script at the root of your site, add the below code and run it!

# Import a standard function, and get the HTML request and response objects.
from Products.PythonScripts.standard import html_quote
request = container.REQUEST
response =  request.response

# Return a string identifying this script.
print "This is the", script.meta_type, '"%s"' % script.getId(),
if script.title:
    print "(%s)" % html_quote(script.title),
print "in", container.absolute_url()

if not hasattr(context, 'portal_type') or context.portal_type != 'Plone Site':
    print 'This script must be run in a Plone Site context!'
    return printed

icon_exprs = {
    'Folder': 'string:${portal_url}/folder_icon.gif',
    'Topic': 'string:${portal_url}/topic_icon.gif',
    'Event': 'string:${portal_url}/event_icon.gif',
    'File': 'string:${portal_url}/file_icon.gif',
    'Image': 'string:${portal_url}/image_icon.gif',
    'News Item': 'string:${portal_url}/newsitem_icon.gif',
    'Document': 'string:${portal_url}/document_icon.gif',
    'Link': 'string:${portal_url}/link_icon.gif'
    }

types_tool = context.portal_types

for portal_type, icon_expr in icon_exprs.items():
    if portal_type not in types_tool:
        print 'WARNING: Default plone portal_type %s not in portal_types tool. Not updating icon' % portal_type
        continue
    print 'INFO: Updating icon_expr for %s to %s' % (portal_type, icon_expr)
    types_tool[portal_type].manage_changeProperties(icon_expr=icon_expr)

return printed