from django.conf import settings  # type: ignore
from django.contrib.auth.models import User  # type: ignore
from django.db.models import ManyToManyField, ForeignKey, \
    URLField, IntegerField, CharField, CASCADE, TextField, \
    DateTimeField, Model, BooleanField  # type: ignore
from django.utils.translation import ugettext_lazy as _  # type: ignore
from parler.models import TranslatedFields, TranslatableModel  # type: ignore


class App(TranslatableModel):
    id = CharField(max_length=128, unique=True, primary_key=True,
                   verbose_name=_('Id'),
                   help_text=_('app id, identical to folder name'))
    categories = ManyToManyField('Category', verbose_name=_('Category'))
    translations = TranslatedFields(
        name=CharField(max_length=128, verbose_name=_('Name'),
                       help_text=_('Rendered app name for users')),
        description=TextField(verbose_name=_('Description'), help_text=_(
            'Will be rendered as Markdown'))
    )
    # resources
    user_docs = URLField(max_length=256, blank=True,
                         verbose_name=_('User documentation url'))
    admin_docs = URLField(max_length=256, blank=True,
                          verbose_name=_('Admin documentation url'))
    developer_docs = URLField(max_length=256, blank=True,
                              verbose_name=_('Developer documentation url'))
    issue_tracker = URLField(max_length=256, blank=True,
                             verbose_name=_('Issue tracker url'))
    website = URLField(max_length=256, blank=True, verbose_name=_('Homepage'))
    created = DateTimeField(auto_now_add=True, editable=False,
                            verbose_name=_('Created at'))
    last_modified = DateTimeField(auto_now=True, editable=False, db_index=True,
                                  verbose_name=_('Updated at'))
    owner = ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('App owner'),
                       on_delete=CASCADE, related_name='owned_apps')
    co_maintainers = ManyToManyField(settings.AUTH_USER_MODEL, blank=True,
                                     verbose_name=_('Co-Maintainers'),
                                     related_name='co_maintained_apps')
    recommendations = ManyToManyField(settings.AUTH_USER_MODEL, blank=True,
                                      verbose_name=_('Recommendations'),
                                      related_name='recommended_apps')
    featured = BooleanField(verbose_name=_('Featured'), default=False)

    class Meta:
        verbose_name = _('App')
        verbose_name_plural = _('Apps')

    def __str__(self) -> str:
        return self.name

    def can_update(self, user: User) -> bool:
        return self.owner == user or user in self.co_maintainers.all()

    def can_delete(self, user: User) -> bool:
        return self.owner == user


class AppRelease(Model):
    version = CharField(max_length=128, verbose_name=_('Version'),
                        help_text=_('Version follows Semantic Versioning'))
    app = ForeignKey('App', on_delete=CASCADE, verbose_name=_('App'),
                     related_name='releases')
    # dependencies
    php_extensions = ManyToManyField('PhpExtension', blank=True,
                                     through='PhpExtensionDependency',
                                     verbose_name=_(
                                         'PHP extension dependency'))
    databases = ManyToManyField('Database', blank=True,
                                through='DatabaseDependency',
                                verbose_name=_('Database dependency'))
    licenses = ManyToManyField('License', verbose_name=_('License'))
    shell_commands = ManyToManyField('ShellCommand', blank=True,
                                     verbose_name=_(
                                         'Shell command dependency'))
    php_version_spec = CharField(max_length=128,
                                 verbose_name=_('PHP version requirement'))
    platform_version_spec = CharField(max_length=128, verbose_name=_(
        'Platform version requirement'))
    min_int_size = IntegerField(blank=True, default=32,
                                verbose_name=_('Minimum Integer Bits'),
                                help_text=_('e.g. 32 for 32bit Integers'))
    checksum = CharField(max_length=64, verbose_name=_('SHA256 checksum'))
    download = URLField(max_length=256, blank=True,
                        verbose_name=_('Archive download Url'))
    created = DateTimeField(auto_now_add=True, editable=False,
                            verbose_name=_('Created at'))
    last_modified = DateTimeField(auto_now=True, editable=False, db_index=True,
                                  verbose_name=_('Updated at'))

    class Meta:
        verbose_name = _('App Release')
        verbose_name_plural = _('App Releases')
        unique_together = (('app', 'version'),)
        ordering = ['-version']

    def can_update(self, user: User) -> bool:
        return self.app.owner == user or user in self.app.co_maintainers.all()

    def can_delete(self, user: User) -> bool:
        return self.can_update(user)

    def __str__(self) -> str:
        return '%s %s' % (self.app, self.version)


class Screenshot(Model):
    url = URLField(max_length=256, verbose_name=_('Image url'))
    app = ForeignKey('App', on_delete=CASCADE, verbose_name=_('App'),
                     related_name='screenshots')
    ordering = IntegerField(verbose_name=_('Ordering'))

    class Meta:
        verbose_name = _('Screenshot')
        verbose_name_plural = _('Screenshots')
        ordering = ['ordering']

    def __str__(self) -> str:
        return self.url


class ShellCommand(Model):
    name = CharField(max_length=128, unique=True, primary_key=True,
                     verbose_name=_('Shell Command'),
                     help_text=_(
                         'Name of a required shell command, e.g. grep'))

    class Meta:
        verbose_name = _('Shell Command')
        verbose_name_plural = _('Shell Commands')

    def __str__(self) -> str:
        return self.name


class Category(TranslatableModel):
    id = CharField(max_length=128, unique=True, primary_key=True,
                   verbose_name=_('Id'),
                   help_text=_(
                       'Category id which is used to identify a '
                       'category. Used to identify categories when '
                       'uploading an app'))
    created = DateTimeField(auto_now_add=True, editable=False,
                            verbose_name=_('Created at'))
    last_modified = DateTimeField(auto_now=True, editable=False, db_index=True,
                                  verbose_name=_('Updated at'))
    translations = TranslatedFields(
        name=CharField(max_length=128, help_text=_(
            'Category name which will be presented to the user'),
                       verbose_name=_('Name')),
        description=TextField(verbose_name=_('Description'),
                              help_text=_('Will be rendered as Markdown'))
    )

    class Meta:
        verbose_name = _('Category')
        verbose_name_plural = _('Categories')

    def __str__(self) -> str:
        return self.name


class License(Model):
    id = CharField(max_length=128, unique=True, primary_key=True,
                   verbose_name=_('Id'),
                   help_text=_(
                       'Key which is used to identify a license'))
    name = CharField(max_length=128, verbose_name=_('Name'),
                     help_text=_(
                         'License name which will be presented to '
                         'the user'))

    class Meta:
        verbose_name = _('License')
        verbose_name_plural = _('Licenses')

    def __str__(self) -> str:
        return self.name


class Database(Model):
    id = CharField(max_length=128, unique=True, primary_key=True,
                   verbose_name=_('Id'),
                   help_text=_('Key which is used to identify a database'))
    name = CharField(max_length=128, verbose_name=_('Name'),
                     help_text=_(
                         'Database name which will be presented to the user'))

    class Meta:
        verbose_name = _('Database')
        verbose_name_plural = _('Databases')

    def __str__(self) -> str:
        return self.name


class DatabaseDependency(Model):
    app_release = ForeignKey('AppRelease', on_delete=CASCADE,
                             verbose_name=_('App release'),
                             related_name='databasedependencies')
    database = ForeignKey('Database', related_name='releasedependencies',
                          on_delete=CASCADE, verbose_name=_('Database'))
    version_spec = CharField(max_length=128,
                             verbose_name=_('Database version requirement'))

    class Meta:
        verbose_name = _('Database Dependency')
        verbose_name_plural = _('Database Dependencies')
        unique_together = (('app_release', 'database', 'version_spec'),)

    def __str__(self) -> str:
        return '%s: %s %s' % (self.app_release, self.database,
                              self.version_spec)


class PhpExtension(Model):
    id = CharField(max_length=128, unique=True, help_text=_('e.g. libxml'),
                   primary_key=True, verbose_name=_('PHP extension'))

    class Meta:
        verbose_name = _('PHP Extension')
        verbose_name_plural = _('PHP Extensions')

    def __str__(self) -> str:
        return self.id


class PhpExtensionDependency(Model):
    app_release = ForeignKey('AppRelease', on_delete=CASCADE,
                             verbose_name=_('App Release'),
                             related_name='phpextensiondependencies')
    php_extension = ForeignKey('PhpExtension', on_delete=CASCADE,
                               verbose_name=_('PHP Extension'),
                               related_name='releasedependencies')
    version_spec = CharField(max_length=128,
                             verbose_name=_('Extension version requirement'))

    class Meta:
        verbose_name = _('PHP Extension Dependency')
        verbose_name_plural = _('PHP Extension Dependencies')
        unique_together = (('app_release', 'php_extension', 'version_spec'),)

    def __str__(self) -> str:
        return '%s: %s %s' % (self.app_release.app, self.php_extension,
                              self.version_spec)

nextcloudappstore/core/models.py
from django.conf import settings  # type: ignore
from django.contrib.auth.models import User  # type: ignore
from django.db.models import ManyToManyField, ForeignKey, \
    URLField, IntegerField, CharField, CASCADE, TextField, \
    DateTimeField, Model, BooleanField  # type: ignore
from django.utils.translation import ugettext_lazy as _  # type: ignore
from parler.models import TranslatedFields, TranslatableModel  # type: ignore


class App(TranslatableModel):
    id = CharField(max_length=128, unique=True, primary_key=True,
                   verbose_name=_('Id'),
                   help_text=_('app id, identical to folder name'))
    categories = ManyToManyField('Category', verbose_name=_('Category'))
    translations = TranslatedFields(
        name=CharField(max_length=128, verbose_name=_('Name'),
                       help_text=_('Rendered app name for users')),
        description=TextField(verbose_name=_('Description'), help_text=_(
            'Will be rendered as Markdown'))
    )
    # resources
    user_docs = URLField(max_length=256, blank=True,
                         verbose_name=_('User documentation url'))
    admin_docs = URLField(max_length=256, blank=True,
                          verbose_name=_('Admin documentation url'))
    developer_docs = URLField(max_length=256, blank=True,
                              verbose_name=_('Developer documentation url'))
    issue_tracker = URLField(max_length=256, blank=True,
                             verbose_name=_('Issue tracker url'))
    website = URLField(max_length=256, blank=True, verbose_name=_('Homepage'))
    created = DateTimeField(auto_now_add=True, editable=False,
                            verbose_name=_('Created at'))
    last_modified = DateTimeField(auto_now=True, editable=False, db_index=True,
                                  verbose_name=_('Updated at'))
    owner = ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('App owner'),
                       on_delete=CASCADE, related_name='owned_apps')
    co_maintainers = ManyToManyField(settings.AUTH_USER_MODEL, blank=True,
                                     verbose_name=_('Co-Maintainers'),
                                     related_name='co_maintained_apps')
    recommendations = ManyToManyField(settings.AUTH_USER_MODEL, blank=True,
                                      verbose_name=_('Recommendations'),
                                      related_name='recommended_apps')
    featured = BooleanField(verbose_name=_('Featured'), default=False)

    class Meta:
        verbose_name = _('App')
        verbose_name_plural = _('Apps')

    def __str__(self) -> str:
        return self.name

    def can_update(self, user: User) -> bool:
        return self.owner == user or user in self.co_maintainers.all()

    def can_delete(self, user: User) -> bool:
        return self.owner == user


class AppRelease(Model):
    version = CharField(max_length=128, verbose_name=_('Version'),
                        help_text=_('Version follows Semantic Versioning'))
    app = ForeignKey('App', on_delete=CASCADE, verbose_name=_('App'),
                     related_name='releases')
    # dependencies
    php_extensions = ManyToManyField('PhpExtension', blank=True,
                                     through='PhpExtensionDependency',
                                     verbose_name=_(
                                         'PHP extension dependency'))
    databases = ManyToManyField('Database', blank=True,
                                through='DatabaseDependency',
                                verbose_name=_('Database dependency'))
    licenses = ManyToManyField('License', verbose_name=_('License'))
    shell_commands = ManyToManyField('ShellCommand', blank=True,
                                     verbose_name=_(
                                         'Shell command dependency'))
    php_version_spec = CharField(max_length=128,
                                 verbose_name=_('PHP version requirement'))
    platform_version_spec = CharField(max_length=128, verbose_name=_(
        'Platform version requirement'))
    min_int_size = IntegerField(blank=True, default=32,
                                verbose_name=_('Minimum Integer Bits'),
                                help_text=_('e.g. 32 for 32bit Integers'))
    checksum = CharField(max_length=64, verbose_name=_('SHA256 checksum'))
    download = URLField(max_length=256, blank=True,
                        verbose_name=_('Archive download Url'))
    created = DateTimeField(auto_now_add=True, editable=False,
                            verbose_name=_('Created at'))
    last_modified = DateTimeField(auto_now=True, editable=False, db_index=True,
                                  verbose_name=_('Updated at'))

    class Meta:
        verbose_name = _('App Release')
        verbose_name_plural = _('App Releases')
        unique_together = (('app', 'version'),)
        ordering = ['-version']

    def can_update(self, user: User) -> bool:
        return self.app.owner == user or user in self.app.co_maintainers.all()

    def can_delete(self, user: User) -> bool:
        return self.can_update(user)

    def __str__(self) -> str:
        return '%s %s' % (self.app, self.version)


class Screenshot(Model):
    url = URLField(max_length=256, verbose_name=_('Image url'))
    app = ForeignKey('App', on_delete=CASCADE, verbose_name=_('App'),
                     related_name='screenshots')
    ordering = IntegerField(verbose_name=_('Ordering'))

    class Meta:
        verbose_name = _('Screenshot')
        verbose_name_plural = _('Screenshots')
        ordering = ['ordering']

    def __str__(self) -> str:
        return self.url


class ShellCommand(Model):
    name = CharField(max_length=128, unique=True, primary_key=True,
                     verbose_name=_('Shell Command'),
                     help_text=_(
                         'Name of a required shell command, e.g. grep'))

    class Meta:
        verbose_name = _('Shell Command')
        verbose_name_plural = _('Shell Commands')

    def __str__(self) -> str:
        return self.name


class Category(TranslatableModel):
    id = CharField(max_length=128, unique=True, primary_key=True,
                   verbose_name=_('Id'),
                   help_text=_(
                       'Category id which is used to identify a '
                       'category. Used to identify categories when '
                       'uploading an app'))
    created = DateTimeField(auto_now_add=True, editable=False,
                            verbose_name=_('Created at'))
    last_modified = DateTimeField(auto_now=True, editable=False, db_index=True,
                                  verbose_name=_('Updated at'))
    translations = TranslatedFields(
        name=CharField(max_length=128, help_text=_(
            'Category name which will be presented to the user'),
                       verbose_name=_('Name')),
        description=TextField(verbose_name=_('Description'),
                              help_text=_('Will be rendered as Markdown'))
    )

    class Meta:
        verbose_name = _('Category')
        verbose_name_plural = _('Categories')
        ordering = ['id']

    def __str__(self) -> str:
        return self.name


class License(Model):
    id = CharField(max_length=128, unique=True, primary_key=True,
                   verbose_name=_('Id'),
                   help_text=_(
                       'Key which is used to identify a license'))
    name = CharField(max_length=128, verbose_name=_('Name'),
                     help_text=_(
                         'License name which will be presented to '
                         'the user'))

    class Meta:
        verbose_name = _('License')
        verbose_name_plural = _('Licenses')

    def __str__(self) -> str:
        return self.name


class Database(Model):
    id = CharField(max_length=128, unique=True, primary_key=True,
                   verbose_name=_('Id'),
                   help_text=_('Key which is used to identify a database'))
    name = CharField(max_length=128, verbose_name=_('Name'),
                     help_text=_(
                         'Database name which will be presented to the user'))

    class Meta:
        verbose_name = _('Database')
        verbose_name_plural = _('Databases')

    def __str__(self) -> str:
        return self.name


class DatabaseDependency(Model):
    app_release = ForeignKey('AppRelease', on_delete=CASCADE,
                             verbose_name=_('App release'),
                             related_name='databasedependencies')
    database = ForeignKey('Database', related_name='releasedependencies',
                          on_delete=CASCADE, verbose_name=_('Database'))
    version_spec = CharField(max_length=128,
                             verbose_name=_('Database version requirement'))

    class Meta:
        verbose_name = _('Database Dependency')
        verbose_name_plural = _('Database Dependencies')
        unique_together = (('app_release', 'database', 'version_spec'),)

    def __str__(self) -> str:
        return '%s: %s %s' % (self.app_release, self.database,
                              self.version_spec)


class PhpExtension(Model):
    id = CharField(max_length=128, unique=True, help_text=_('e.g. libxml'),
                   primary_key=True, verbose_name=_('PHP extension'))

    class Meta:
        verbose_name = _('PHP Extension')
        verbose_name_plural = _('PHP Extensions')

    def __str__(self) -> str:
        return self.id


class PhpExtensionDependency(Model):
    app_release = ForeignKey('AppRelease', on_delete=CASCADE,
                             verbose_name=_('App Release'),
                             related_name='phpextensiondependencies')
    php_extension = ForeignKey('PhpExtension', on_delete=CASCADE,
                               verbose_name=_('PHP Extension'),
                               related_name='releasedependencies')
    version_spec = CharField(max_length=128,
                             verbose_name=_('Extension version requirement'))

    class Meta:
        verbose_name = _('PHP Extension Dependency')
        verbose_name_plural = _('PHP Extension Dependencies')
        unique_together = (('app_release', 'php_extension', 'version_spec'),)

    def __str__(self) -> str:
        return '%s: %s %s' % (self.app_release.app, self.php_extension,
                              self.version_spec)

nextcloudappstore/core/models.py
diff --git a/nextcloudappstore/core/api/v1/release/info.xsd b/nextcloudappstore/core/api/v1/release/info.xsd
index b3676d7e33e..67d0f4eae68 100644
--- a/nextcloudappstore/core/api/v1/release/info.xsd
+++ b/nextcloudappstore/core/api/v1/release/info.xsd
@@ -268,10 +268,15 @@
 
     <xs:simpleType name="category">
         <xs:restriction base="xs:string">
+            <xs:enumeration value="customization"/>
+            <xs:enumeration value="files"/>
+            <xs:enumeration value="integration"/>
+            <xs:enumeration value="monitoring"/>
             <xs:enumeration value="multimedia"/>
-            <xs:enumeration value="pim"/>
+            <xs:enumeration value="office"/>
+            <xs:enumeration value="organization"/>
+            <xs:enumeration value="social"/>
             <xs:enumeration value="tools"/>
-            <xs:enumeration value="games"/>
         </xs:restriction>
     </xs:simpleType>
 
diff --git a/nextcloudappstore/core/fixtures/categories.json b/nextcloudappstore/core/fixtures/categories.json
index 7b87f80b439..1b2cb8e226e 100644
--- a/nextcloudappstore/core/fixtures/categories.json
+++ b/nextcloudappstore/core/fixtures/categories.json
@@ -5,6 +5,7 @@
         "fields": {
             "language_code": "en",
             "name": "Tools",
+            "description": "Everything else",
             "master": "tools"
         }
     },
@@ -14,6 +15,7 @@
         "fields": {
             "language_code": "de",
             "name": "Werkzeuge",
+            "description": "",
             "master": "tools"
         }
     },
@@ -23,96 +25,140 @@
         "fields": {
             "language_code": "fr",
             "name": "Outil",
+            "description": "",
             "master": "tools"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 4,
+        "pk": 10,
         "fields": {
             "language_code": "en",
-            "name": "Games",
-            "master": "games"
+            "name": "Multimedia",
+            "description": "Audio, video and picture apps",
+            "master": "multimedia"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 5,
+        "pk": 11,
         "fields": {
             "language_code": "de",
-            "name": "Spiele",
-            "master": "games"
+            "name": "Multimedia",
+            "description": "",
+            "master": "multimedia"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 6,
+        "pk": 12,
         "fields": {
             "language_code": "fr",
-            "name": "Jeux",
-            "master": "games"
+            "name": "Multimedia",
+            "description": "",
+            "master": "multimedia"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 7,
+        "pk": 13,
         "fields": {
             "language_code": "en",
-            "name": "PIM",
-            "master": "pim"
+            "name": "Files",
+            "description": "File mangement and Files app extension apps",
+            "master": "files"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 8,
+        "pk": 14,
         "fields": {
-            "language_code": "de",
-            "name": "PIM",
-            "master": "pim"
+            "language_code": "en",
+            "name": "Office & text",
+            "description": "Office and text processing apps",
+            "master": "office"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 9,
+        "pk": 15,
         "fields": {
-            "language_code": "fr",
-            "name": "PIM",
-            "master": "pim"
+            "language_code": "en",
+            "name": "Customization",
+            "description": "Themes, layout and UX change apps",
+            "master": "customization"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 10,
+        "pk": 16,
         "fields": {
             "language_code": "en",
-            "name": "Multimedia",
-            "master": "multimedia"
+            "name": "Social & communication",
+            "description": "Messaging, contact management and social media apps",
+            "master": "social"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 11,
+        "pk": 17,
         "fields": {
-            "language_code": "de",
-            "name": "Multimedia",
-            "master": "multimedia"
+            "language_code": "en",
+            "name": "Organization",
+            "description": "Time management, Todo list and calendar apps",
+            "master": "organization"
         }
     },
     {
         "model": "core.categorytranslation",
-        "pk": 12,
+        "pk": 18,
         "fields": {
-            "language_code": "fr",
-            "name": "Multimedia",
-            "master": "multimedia"
+            "language_code": "en",
+            "name": "Integration",
+            "description": "Apps that connect Nextcloud with other services and platforms",
+            "master": "integration"
+        }
+    },
+    {
+        "model": "core.categorytranslation",
+        "pk": 19,
+        "fields": {
+            "language_code": "en",
+            "name": "Monitoring",
+            "description": "Data statistics, system diagnostics and activity apps",
+            "master": "monitoring"
         }
     },
     {
         "model": "core.category",
-        "pk": "games",
+        "pk": "customization",
         "fields": {
-            "created": "2016-06-11T10:37:24Z",
-            "last_modified": "2016-06-11T10:37:24Z"
+            "created": "2016-07-02T09:54:54.939Z",
+            "last_modified": "2016-07-02T09:54:54.939Z"
+        }
+    },
+    {
+        "model": "core.category",
+        "pk": "files",
+        "fields": {
+            "created": "2016-07-02T09:25:25.325Z",
+            "last_modified": "2016-07-02T09:27:25.338Z"
+        }
+    },
+    {
+        "model": "core.category",
+        "pk": "integration",
+        "fields": {
+            "created": "2016-07-02T09:59:44.560Z",
+            "last_modified": "2016-07-02T09:59:44.560Z"
+        }
+    },
+    {
+        "model": "core.category",
+        "pk": "monitoring",
+        "fields": {
+            "created": "2016-07-02T10:00:58.515Z",
+            "last_modified": "2016-07-02T10:00:58.515Z"
         }
     },
     {
@@ -120,15 +166,31 @@
         "pk": "multimedia",
         "fields": {
             "created": "2016-06-11T10:37:24Z",
-            "last_modified": "2016-06-11T10:37:24Z"
+            "last_modified": "2016-07-02T10:02:52.864Z"
         }
     },
     {
         "model": "core.category",
-        "pk": "pim",
+        "pk": "office",
         "fields": {
-            "created": "2016-06-11T10:37:24Z",
-            "last_modified": "2016-06-11T10:37:24Z"
+            "created": "2016-07-02T09:26:27.216Z",
+            "last_modified": "2016-07-02T09:28:24.928Z"
+        }
+    },
+    {
+        "model": "core.category",
+        "pk": "organization",
+        "fields": {
+            "created": "2016-07-02T09:57:57.226Z",
+            "last_modified": "2016-07-02T09:57:57.226Z"
+        }
+    },
+    {
+        "model": "core.category",
+        "pk": "social",
+        "fields": {
+            "created": "2016-07-02T09:57:18.394Z",
+            "last_modified": "2016-07-02T09:57:18.394Z"
         }
     },
     {
@@ -136,7 +198,7 @@
         "pk": "tools",
         "fields": {
             "created": "2016-06-11T10:37:24Z",
-            "last_modified": "2016-06-11T10:37:24Z"
+            "last_modified": "2016-07-02T10:01:45.369Z"
         }
     }
 ]
diff --git a/nextcloudappstore/core/models.py b/nextcloudappstore/core/models.py
index c65065c27ef..a1f63837896 100644
--- a/nextcloudappstore/core/models.py
+++ b/nextcloudappstore/core/models.py
@@ -155,6 +155,7 @@ class Category(TranslatableModel):
     class Meta:
         verbose_name = _('Category')
         verbose_name_plural = _('Categories')
+        ordering = ['id']
 
     def __str__(self) -> str:
         return self.name

What categories do we need?
Currently allowed categories include:
- multimedia
- pim
- tools
- games

Anything else?

