diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetForm.java
new file mode 100644
index 000000000..562b16594
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetForm.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package com.arsdigita.cms.ui.assets;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class AssetForm {
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/assets/AssetType.java b/ccm-cms/src/main/java/org/librecms/assets/AssetType.java
new file mode 100644
index 000000000..4512e47a5
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/assets/AssetType.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.assets;
+
+import com.arsdigita.cms.ui.assets.AssetForm;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to describe an asset type. It provides the class name of edit/create
+ * form for the asset. Can also be used to customise the keys and bundles used
+ * to retrieve the label and the description of an asset type.
+ *
+ * The only required parameter is {@link #assetForm()} which provides the form
+ * for editing and creating assets of the annotated type. The other parameters
+ * can be left empty. If left empty a internal default value will be used.
+ *
+ * This annotation should only be used on subclasses of
+ * {@link org.librecms.contentsection.Asset}.
+ *
+ * @author Jens Pelzetter
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AssetType {
+
+ /**
+ * The form for editing and creating an asset of the annotated class. This
+ * parameter is required.
+ *
+ * @return The form for editing and creating assets of the annotated sub
+ * class {@link org.librecms.contentsection.Asset}.
+ */
+ Class extends AssetForm> assetForm();
+
+ /**
+ * The key for the localised label of the asset type. If not set the default
+ * value {@code label} is used.
+ *
+ * @return The key for the localised label of the asset type.
+ */
+ String labelKey() default "";
+
+ /**
+ * The bundle which provides the label of the asset type. If not set the
+ * default value is used. Default is the fully qualified class name of the
+ * annotated class with suffix {@code Bundle}. For example the default
+ * bundle for the asset type {@link org.librecms.assets.Image} is
+ * {@code org.librecms.assets.ImageBundle}.
+ *
+ * @return The fully qualified name of the bundle providing the label for
+ * the asset type.
+ */
+ String labelBundle() default "";
+
+ /**
+ * The key for the description of the asset type in the description bundle.
+ * Default value is {@code descripion}.
+ *
+ * @return The key for the description of the asset type.
+ */
+ String descriptionKey() default "";
+
+ /**
+ * The bundle which provides the description of the asset type. If not set
+ * the default value is used. Default is the fully qualified class name of
+ * the annotated class with the suffix {@code Bundle}. For example the
+ * default bundle for the asset type {@link org.librecms.assets.Image} is
+ * {@code org.librecms.assets.ImageBundle}.
+ *
+ * @return The fully qualified name of the bundle providing the description
+ * of the asset type.
+ */
+ String descriptionBundle() default "";
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/assets/AssetTypeInfo.java b/ccm-cms/src/main/java/org/librecms/assets/AssetTypeInfo.java
new file mode 100644
index 000000000..19c8a036b
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/assets/AssetTypeInfo.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.assets;
+
+import com.arsdigita.cms.ui.assets.AssetForm;
+
+import org.librecms.contentsection.Asset;
+
+import java.util.Objects;
+
+/**
+ * A easy to use encapsulation for the informations about an asset type.
+ *
+ * @author Jens Pelzetter
+ */
+public class AssetTypeInfo {
+
+ /**
+ * The bundle which provides the localised label of the asset type.
+ */
+ private String labelBundle;
+
+ /**
+ * The key of the label in the {@link #labelBundle}.
+ */
+ private String labelKey;
+
+ /**
+ * The bundle which provides the localised description of the asset type.
+ */
+ private String descriptionBundle;
+
+ /**
+ * The key of the description in the {@link #descriptionBundle}.
+ */
+ private String descriptionKey;
+
+ /**
+ * The class of the asset type.
+ */
+ private Class extends Asset> assetClass;
+
+ /**
+ * The form for editing and creating asset of the type described.
+ */
+ private Class extends AssetForm> assetForm;
+
+ public String getLabelBundle() {
+ return labelBundle;
+ }
+
+ public void setLabelBundle(final String labelBundle) {
+ this.labelBundle = labelBundle;
+ }
+
+ public String getLabelKey() {
+ return labelKey;
+ }
+
+ public void setLabelKey(final String labelKey) {
+ this.labelKey = labelKey;
+ }
+
+ public String getDescriptionBundle() {
+ return descriptionBundle;
+ }
+
+ public void setDescriptionBundle(final String descriptionBundle) {
+ this.descriptionBundle = descriptionBundle;
+ }
+
+ public String getDescriptionKey() {
+ return descriptionKey;
+ }
+
+ public void setDescriptionKey(final String descriptionKey) {
+ this.descriptionKey = descriptionKey;
+ }
+
+ public Class extends Asset> getAssetClass() {
+ return assetClass;
+ }
+
+ public void setAssetClass(final Class extends Asset> assetClass) {
+ this.assetClass = assetClass;
+ }
+
+ public Class extends AssetForm> getAssetForm() {
+ return assetForm;
+ }
+
+ public void setAssetForm(final Class extends AssetForm> assetForm) {
+ this.assetForm = assetForm;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 59 * hash + Objects.hashCode(labelBundle);
+ hash = 59 * hash + Objects.hashCode(labelKey);
+ hash = 59 * hash + Objects.hashCode(descriptionBundle);
+ hash = 59 * hash + Objects.hashCode(descriptionKey);
+ hash = 59 * hash + Objects.hashCode(assetClass);
+ hash = 59 * hash + Objects.hashCode(assetForm);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof AssetTypeInfo)) {
+ return false;
+ }
+ final AssetTypeInfo other = (AssetTypeInfo) obj;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (!Objects.equals(labelBundle, other.getLabelBundle())) {
+ return false;
+ }
+ if (!Objects.equals(labelKey, other.getLabelKey())) {
+ return false;
+ }
+ if (!Objects.equals(descriptionBundle, other.getDescriptionBundle())) {
+ return false;
+ }
+ if (!Objects.equals(descriptionKey, other.getDescriptionKey())) {
+ return false;
+ }
+ if (!Objects.equals(assetClass, other.getAssetClass())) {
+ return false;
+ }
+ return Objects.equals(assetForm, other.getAssetForm());
+ }
+
+ private boolean canEqual(final Object obj) {
+ return obj instanceof AssetTypeInfo;
+ }
+
+ @Override
+ public final String toString() {
+ return toString("");
+ }
+
+ public String toString(final String data) {
+ return String.format("%s{ "
+ + "labelBundle = \"%s\", "
+ + "labelKey = \"%s\", "
+ + "descriptionBundle = \"%s\", "
+ + "descriptionKey = \"%s\", "
+ + "assetClass = \"%s\", "
+ + "assetForm = \"%s\"%s "
+ + " }",
+ super.toString(),
+ labelBundle,
+ labelKey,
+ descriptionBundle,
+ descriptionKey,
+ Objects.toString(assetClass),
+ Objects.toString(assetForm),
+ data);
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/assets/AssetTypes.java b/ccm-cms/src/main/java/org/librecms/assets/AssetTypes.java
new file mode 100644
index 000000000..568ebebe8
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/assets/AssetTypes.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.assets;
+
+import org.librecms.contentsection.Asset;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used on a module class like {@link org.librecms.Cms} to inform to system
+ * which asset types are provided by the module.
+ *
+ * @author Jens Pelzetter
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AssetTypes {
+
+ Class extends Asset>[] value();
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/assets/AssetTypesManager.java b/ccm-cms/src/main/java/org/librecms/assets/AssetTypesManager.java
new file mode 100644
index 000000000..32a38ad12
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/assets/AssetTypesManager.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.assets;
+
+import org.libreccm.modules.CcmModule;
+import org.librecms.contentsection.Asset;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.RequestScoped;
+
+/**
+ * Provides informations about the available asset types.
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class AssetTypesManager {
+
+ private static final String DEFAULT_DESCRIPTION_KEY = "description";
+ private static final String DEFAULT_LABEL_KEY = "label";
+
+ /**
+ * A list of all asset types available.
+ */
+ private List availableAssetTypes;
+
+ /**
+ * Initialises the class (is called by CDI). This method is called by the
+ * CDI container after an instance of this class has been created by the CDI
+ * container. This method creates the list {@link #availableAssetTypes}.
+ */
+ @PostConstruct
+ protected void initialize() {
+
+ final ServiceLoader modules = ServiceLoader
+ .load(CcmModule.class);
+
+ final SortedSet> assetTypes = new TreeSet<>(
+ (type1, type2) -> type1.getName().compareTo(type2.getName()));
+
+ for (final CcmModule module : modules) {
+ final AssetTypes annotation = module
+ .getClass()
+ .getAnnotation(AssetTypes.class);
+
+ if (annotation == null) {
+ continue;
+ }
+
+ assetTypes.addAll(Arrays.asList(annotation.value()));
+ }
+
+ availableAssetTypes = assetTypes
+ .stream()
+ .filter(type -> type.getAnnotation(AssetType.class) != null)
+ .map(assetTypeClass -> createAssetTypeInfo(assetTypeClass))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Helper method for creating the info object for a asset type.
+ *
+ * @param assetTypeClass The class which provides the implementation of the
+ * asset type.
+ *
+ * @return A {@link AssetTypeInfo} object describing the asset type.
+ */
+ private AssetTypeInfo createAssetTypeInfo(
+ final Class extends Asset> assetTypeClass) {
+
+ Objects.requireNonNull(assetTypeClass);
+
+ final AssetTypeInfo assetTypeInfo = new AssetTypeInfo();
+ assetTypeInfo.setAssetClass(assetTypeClass);
+
+ final String defaultBundleName = String.format("%sBundle",
+ assetTypeClass.getName());
+ final AssetType assetType = assetTypeClass
+ .getAnnotation(AssetType.class);
+
+ if (assetType == null) {
+ assetTypeInfo.setLabelBundle(defaultBundleName);
+ assetTypeInfo.setDescriptionBundle(defaultBundleName);
+ assetTypeInfo.setLabelKey(DEFAULT_LABEL_KEY);
+ assetTypeInfo.setDescriptionKey(DEFAULT_DESCRIPTION_KEY);
+ } else {
+ if (assetType.labelBundle().isEmpty()) {
+ assetTypeInfo.setLabelBundle(defaultBundleName);
+ } else {
+ assetTypeInfo.setLabelBundle(assetType.labelBundle());
+ }
+
+ if (assetType.labelKey().isEmpty()) {
+ assetTypeInfo.setLabelKey(DEFAULT_LABEL_KEY);
+ } else {
+ assetTypeInfo.setLabelKey(assetType.labelKey());
+ }
+
+ if (assetType.descriptionBundle().isEmpty()) {
+ assetTypeInfo.setDescriptionBundle(defaultBundleName);
+ } else {
+ assetTypeInfo.setDescriptionKey(assetType.descriptionBundle());
+ }
+
+ if (assetType.descriptionKey().isEmpty()) {
+ assetTypeInfo.setDescriptionKey(DEFAULT_DESCRIPTION_KEY);
+ } else {
+ assetTypeInfo.setDescriptionKey(assetType.descriptionKey());
+ }
+
+ assetTypeInfo.setAssetForm(assetType.assetForm());
+ }
+
+ return assetTypeInfo;
+ }
+
+ /**
+ * Retrieves a list of all available asset types.
+ *
+ * @return A list all available asset types.
+ */
+ public List getAvailableAssetTypes() {
+ return Collections.unmodifiableList(availableAssetTypes);
+ }
+
+ /**
+ * Get the {@link AssetTypeInfo} for a specific type.
+ *
+ * @param assetTypeClass The class representing the asset type.
+ *
+ * @return A {@link AssetTypeInfo} object describing the asset type.
+ */
+ public AssetTypeInfo getAssetTypeInfo(
+ final Class extends Asset> assetTypeClass) {
+
+ Objects.requireNonNull(assetTypeClass);
+
+ return createAssetTypeInfo(assetTypeClass);
+ }
+
+ /**
+ * Convenient method for getting the {@link AssetTypeInfo} about a specific
+ * asset type.
+ *
+ * @param assetTypeClass The fully qualified name of the class representing
+ * the asset type.
+ *
+ * @return A {@link AssetTypeInfo} object describing the asset type.
+ *
+ * @throws IllegalArgumentException If no class with the provided name
+ * exists or the class is not a subclass of
+ * {@link Asset}.
+ */
+ @SuppressWarnings("unchecked")
+ public AssetTypeInfo getAssetTypeInfo(final String assetTypeClass) {
+
+ Objects.requireNonNull(assetTypeClass);
+
+ final Class> clazz;
+ try {
+ clazz = Class.forName(assetTypeClass);
+ } catch (ClassNotFoundException ex) {
+ throw new IllegalArgumentException(String.format(
+ "There is not class \"%s\".", assetTypeClass),
+ ex);
+ }
+
+ if (!Asset.class.isAssignableFrom(clazz)) {
+ throw new IllegalArgumentException(String.format(
+ "Class \"%s\" is not a subclass of of \"%s\".",
+ assetTypeClass,
+ Asset.class.getName()));
+ }
+
+ return getAssetTypeInfo((Class extends Asset>) clazz);
+ }
+
+}