diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py
index ec132d3dd4453c2710cf181d9fbda302e98005c3..6bbf98dffecc06f1664e6a5ee1cc0fcfc0ccc8be 100644
--- a/SciPost_v1/settings/base.py
+++ b/SciPost_v1/settings/base.py
@@ -86,9 +86,11 @@ INSTALLED_APPS = (
     'commentaries',
     'comments',
     'journals',
+    'news',
     'scipost',
     'submissions',
     'theses',
+    'virtualmeetings',
     'webpack_loader'
 )
 
diff --git a/SciPost_v1/urls.py b/SciPost_v1/urls.py
index 9b85d34e8e3967fdfc82a646d9d8c76443eb3c9a..9391da29315f2d285bc84dc132c7e7dafa617cb8 100644
--- a/SciPost_v1/urls.py
+++ b/SciPost_v1/urls.py
@@ -31,5 +31,5 @@ urlpatterns = [
     url(r'^submission/', include('submissions.urls', namespace="submissions")),
     url(r'^theses/', include('theses.urls', namespace="theses")),
     url(r'^thesis/', include('theses.urls', namespace="theses")),
-    # url(r'^captcha/', include('captcha.urls')),
+    url(r'^news/', include('news.urls', namespace="news")),
 ]
diff --git a/news/__init__.py b/news/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/news/admin.py b/news/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..43253c6cc5b48d7b606f34ea7e5f57f639625ee2
--- /dev/null
+++ b/news/admin.py
@@ -0,0 +1,10 @@
+from django.contrib import admin
+
+from .models import NewsItem
+
+
+class NewsItemAdmin(admin.ModelAdmin):
+    search_fields = ['blurb', 'followup_link_text']
+
+
+admin.site.register(NewsItem, NewsItemAdmin)
diff --git a/news/apps.py b/news/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a7b92d0f844e1bd89c73e7bba369b07298ae70a
--- /dev/null
+++ b/news/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class NewsConfig(AppConfig):
+    name = 'news'
diff --git a/news/migrations/0001_initial.py b/news/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..4df07209043385758fe45a5f4a24096fbfd20415
--- /dev/null
+++ b/news/migrations/0001_initial.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-03-06 07:04
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('scipost', '0039_auto_20170306_0804'),
+    ]
+
+    state_operations = [
+        migrations.CreateModel(
+            name='NewsItem',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('date', models.DateField()),
+                ('headline', models.CharField(max_length=300)),
+                ('blurb', models.TextField()),
+                ('followup_link', models.URLField(blank=True, null=True)),
+                ('followup_link_text', models.CharField(blank=True, max_length=300, null=True)),
+            ],
+            options={
+                'db_table': 'scipost_newsitem',
+            },
+        ),
+    ]
+
+    operations = [
+        migrations.SeparateDatabaseAndState(state_operations=state_operations)
+    ]
diff --git a/news/migrations/__init__.py b/news/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/news/models.py b/news/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..d421b8c4bbae2b55c595dd0d4fcec7347509dc6f
--- /dev/null
+++ b/news/models.py
@@ -0,0 +1,52 @@
+from django.db import models
+from django.template import Template, Context
+
+
+class NewsItem(models.Model):
+    date = models.DateField()
+    headline = models.CharField(max_length=300)
+    blurb = models.TextField()
+    followup_link = models.URLField(blank=True, null=True)
+    followup_link_text = models.CharField(max_length=300, blank=True, null=True)
+
+    class Meta:
+        db_table = 'scipost_newsitem'
+
+    def __str__(self):
+        return self.date.strftime('%Y-%m-%d') + ', ' + self.headline
+
+    def descriptor_full(self):
+        """ For News page. """
+        descriptor = ('<div class="flex-greybox640">'
+                      '<h3 class="NewsHeadline">{{ headline }}</h3>'
+                      '<p>{{ date }}</p>'
+                      '<p>{{ blurb }}</p>'
+                      )
+        context = Context({'headline': self.headline,
+                           'date': self.date.strftime('%Y-%m-%d'),
+                           'blurb': self.blurb, })
+        if self.followup_link:
+            descriptor += '<p><a href="{{ followup_link }}">{{ followup_link_text }}</a></p>'
+            context['followup_link'] = self.followup_link
+            context['followup_link_text'] = self.followup_link_text
+        descriptor += '</div>'
+        template = Template(descriptor)
+        return template.render(context)
+
+    def descriptor_small(self):
+        """ For index page. """
+        descriptor = ('<h3 class="NewsHeadline">{{ headline }}</h3>'
+                      '<div class="p-2">'
+                      '<p>{{ date }}</p>'
+                      '<p>{{ blurb }}</p>'
+                      )
+        context = Context({'headline': self.headline,
+                           'date': self.date.strftime('%Y-%m-%d'),
+                           'blurb': self.blurb, })
+        if self.followup_link:
+            descriptor += '<p><a href="{{ followup_link }}">{{ followup_link_text }}</a></p>'
+            context['followup_link'] = self.followup_link
+            context['followup_link_text'] = self.followup_link_text
+        descriptor += '</div>'
+        template = Template(descriptor)
+        return template.render(context)
diff --git a/news/tests.py b/news/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6
--- /dev/null
+++ b/news/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/news/urls.py b/news/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..a84f2906bb39ef2937fe1a6ca284e8e6a77368e7
--- /dev/null
+++ b/news/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls import url
+
+from . import views
+
+urlpatterns = [
+    url(r'^$', views.news, name='news'),
+]
diff --git a/news/views.py b/news/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e6773d73118132b4eeeb2ca1c21b4aa6b1dd138
--- /dev/null
+++ b/news/views.py
@@ -0,0 +1,9 @@
+from django.shortcuts import render
+
+from .models import NewsItem
+
+
+def news(request):
+    newsitems = NewsItem.objects.all().order_by('-date')
+    context = {'newsitems': newsitems}
+    return render(request, 'scipost/news.html', context)
diff --git a/scipost/admin.py b/scipost/admin.py
index 799517e9b63c9541937e8669a9945ea2361b77f9..ccb5be7f2600f04a7c149736635d12225bef97aa 100644
--- a/scipost/admin.py
+++ b/scipost/admin.py
@@ -5,60 +5,74 @@ from django.contrib.auth.models import User, Permission
 
 from guardian.admin import GuardedModelAdmin
 
-from scipost.models import *
+from scipost.models import Contributor, Remark, List,\
+                           DraftInvitation, Node, Arc, Graph, Team, AffiliationObject,\
+                           SupportingPartner, SPBMembershipAgreement, RegistrationInvitation,\
+                           AuthorshipClaim, PrecookedEmail
+from virtualmeetings.models import VGM, Feedback, Nomination, Motion
+
 
 class ContributorInline(admin.StackedInline):
     model = Contributor
 
+
 class UserAdmin(UserAdmin):
     inlines = [
         ContributorInline,
         ]
     search_fields = ['last_name', 'email']
 
+
 admin.site.unregister(User)
 admin.site.register(User, UserAdmin)
 
 
-class VGMAdmin(admin.ModelAdmin):
-    search_fields = ['start_date']
-
-admin.site.register(VGM, VGMAdmin)
-
-
-class FeedbackAdmin(admin.ModelAdmin):
-    search_fields = ['feedback', 'by']
-
-admin.site.register(Feedback, FeedbackAdmin)
-
-
-class NominationAdmin(admin.ModelAdmin):
-    search_fields = ['last_name', 'first_name', 'by']
-
-admin.site.register(Nomination, NominationAdmin)
-
-
-class MotionAdmin(admin.ModelAdmin):
-    search_fields = ['background', 'motion', 'put_forward_by']
-
-admin.site.register(Motion, MotionAdmin)
+# class VGMAdmin(admin.ModelAdmin):
+#     search_fields = ['start_date']
+#
+#
+# admin.site.register(VGM, VGMAdmin)
+#
+#
+# class FeedbackAdmin(admin.ModelAdmin):
+#     search_fields = ['feedback', 'by']
+#
+#
+# admin.site.register(Feedback, FeedbackAdmin)
+#
+#
+# class NominationAdmin(admin.ModelAdmin):
+#     search_fields = ['last_name', 'first_name', 'by']
+#
+#
+# admin.site.register(Nomination, NominationAdmin)
+#
+#
+# class MotionAdmin(admin.ModelAdmin):
+#     search_fields = ['background', 'motion', 'put_forward_by']
+#
+#
+# admin.site.register(Motion, MotionAdmin)
 
 
 class RemarkAdmin(admin.ModelAdmin):
     search_fields = ['contributor', 'remark']
 
+
 admin.site.register(Remark, RemarkAdmin)
 
 
 class DraftInvitationAdmin(admin.ModelAdmin):
     search_fields = ['first_name', 'last_name', 'email', 'processed']
 
+
 admin.site.register(DraftInvitation, DraftInvitationAdmin)
 
 
 class RegistrationInvitationAdmin(admin.ModelAdmin):
     search_fields = ['first_name', 'last_name', 'email', 'invitation_key']
 
+
 admin.site.register(RegistrationInvitation, RegistrationInvitationAdmin)
 
 
@@ -69,34 +83,26 @@ admin.site.register(Permission)
 class PrecookedEmailAdmin(admin.ModelAdmin):
     search_fields = ['email_subject', 'email_text', 'emailed_to']
 
-admin.site.register(PrecookedEmail, PrecookedEmailAdmin)
-
-
-class NewsItemAdmin(admin.ModelAdmin):
-    search_fields = ['blurb', 'followup_link_text']
 
-admin.site.register(NewsItem, NewsItemAdmin)
+admin.site.register(PrecookedEmail, PrecookedEmailAdmin)
 
 
 class ListAdmin(GuardedModelAdmin):
     search_fields = ['owner', 'title']
 
-admin.site.register(List, ListAdmin)
 
+admin.site.register(List, ListAdmin)
 admin.site.register(Team)
 
-#admin.site.register(Graph)
-
-#admin.site.register(Node)
-
-#admin.site.register(Arc)
 
 class NodeInline(admin.StackedInline):
     model = Node
 
+
 class ArcInline(admin.StackedInline):
     model = Arc
 
+
 class GraphAdmin(GuardedModelAdmin):
     inlines = [
         NodeInline,
@@ -104,18 +110,21 @@ class GraphAdmin(GuardedModelAdmin):
         ]
     search_fields = ['owner___user__last_name', 'title']
 
+
 admin.site.register(Graph, GraphAdmin)
 
 
 class AffiliationObjectAdmin(admin.ModelAdmin):
     search_fields = ['country', 'institution', 'subunit']
 
+
 admin.site.register(AffiliationObject, AffiliationObjectAdmin)
 
 
 class SPBMembershipAgreementInline(admin.StackedInline):
     model = SPBMembershipAgreement
 
+
 class SupportingPartnerAdmin(admin.ModelAdmin):
     search_fields = ['institution', 'institution_acronym',
                      'institution_address', 'contact_person']
@@ -123,4 +132,5 @@ class SupportingPartnerAdmin(admin.ModelAdmin):
         SPBMembershipAgreementInline,
     ]
 
+
 admin.site.register(SupportingPartner, SupportingPartnerAdmin)
diff --git a/scipost/feeds.py b/scipost/feeds.py
index 297ea1c779d387687b71a39c3a02abe3f7809c70..29ea0a31d6fced705e0c8fd6ac77381e7ed818f8 100644
--- a/scipost/feeds.py
+++ b/scipost/feeds.py
@@ -5,10 +5,10 @@ from django.utils.feedgenerator import Atom1Feed
 from django.core.urlresolvers import reverse
 from django.db.models import Q
 
-from scipost.models import NewsItem
 from scipost.models import subject_areas_dict
 from comments.models import Comment
 from journals.models import Publication
+from news.models import NewsItem
 from submissions.models import Submission, SUBMISSION_STATUS_PUBLICLY_INVISIBLE
 
 
diff --git a/scipost/forms.py b/scipost/forms.py
index 7c628d2f224eff4db20149f95e7765ffc3137e63..04f36192dbf87b171561b533c64707c7afffa624 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -16,8 +16,8 @@ from .models import TITLE_CHOICES, SCIPOST_FROM_ADDRESSES, ARC_LENGTHS,\
                      Contributor, DraftInvitation, RegistrationInvitation,\
                      SupportingPartner, SPBMembershipAgreement,\
                      UnavailabilityPeriod, PrecookedEmail,\
-                     List, Team, Graph, Node,\
-                     Feedback, Nomination, Motion
+                     List, Team, Graph, Node
+from virtualmeetings.models import Feedback, Nomination, Motion
 
 from journals.models import Publication
 from submissions.models import SUBMISSION_STATUS_PUBLICLY_UNLISTED
diff --git a/scipost/migrations/0039_auto_20170306_0804.py b/scipost/migrations/0039_auto_20170306_0804.py
new file mode 100644
index 0000000000000000000000000000000000000000..49894a205825fdbe083d86f688b835138d7516ce
--- /dev/null
+++ b/scipost/migrations/0039_auto_20170306_0804.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-03-06 07:04
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0038_nomination_webpage'),
+    ]
+
+    state_operations = [
+        migrations.RemoveField(
+            model_name='feedback',
+            name='VGM',
+        ),
+        migrations.RemoveField(
+            model_name='feedback',
+            name='by',
+        ),
+        migrations.RemoveField(
+            model_name='motion',
+            name='VGM',
+        ),
+        migrations.RemoveField(
+            model_name='motion',
+            name='in_agreement',
+        ),
+        migrations.RemoveField(
+            model_name='motion',
+            name='in_disagreement',
+        ),
+        migrations.RemoveField(
+            model_name='motion',
+            name='in_notsure',
+        ),
+        migrations.RemoveField(
+            model_name='motion',
+            name='put_forward_by',
+        ),
+        migrations.DeleteModel(
+            name='NewsItem',
+        ),
+        migrations.RemoveField(
+            model_name='nomination',
+            name='VGM',
+        ),
+        migrations.RemoveField(
+            model_name='nomination',
+            name='by',
+        ),
+        migrations.RemoveField(
+            model_name='nomination',
+            name='in_agreement',
+        ),
+        migrations.RemoveField(
+            model_name='nomination',
+            name='in_disagreement',
+        ),
+        migrations.RemoveField(
+            model_name='nomination',
+            name='in_notsure',
+        ),
+        migrations.AlterField(
+            model_name='remark',
+            name='feedback',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Feedback'),
+        ),
+        migrations.AlterField(
+            model_name='remark',
+            name='motion',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Motion'),
+        ),
+        migrations.AlterField(
+            model_name='remark',
+            name='nomination',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.Nomination'),
+        ),
+        migrations.DeleteModel(
+            name='Feedback',
+        ),
+        migrations.DeleteModel(
+            name='Motion',
+        ),
+        migrations.DeleteModel(
+            name='Nomination',
+        ),
+        migrations.DeleteModel(
+            name='VGM',
+        ),
+    ]
+
+    operations = [
+        migrations.SeparateDatabaseAndState(state_operations=state_operations)
+    ]
diff --git a/scipost/models.py b/scipost/models.py
index a29a48f14a27c5181be06a759e02af470a485aba..4bde7f7afa12b3095ad0c2aff68273a00ba6db5a 100644
--- a/scipost/models.py
+++ b/scipost/models.py
@@ -251,11 +251,11 @@ class UnavailabilityPeriod(models.Model):
 
 class Remark(models.Model):
     contributor = models.ForeignKey(Contributor, on_delete=models.CASCADE)
-    feedback = models.ForeignKey('scipost.Feedback', on_delete=models.CASCADE,
+    feedback = models.ForeignKey('virtualmeetings.Feedback', on_delete=models.CASCADE,
                                  blank=True, null=True)
-    nomination = models.ForeignKey('scipost.Nomination', on_delete=models.CASCADE,
+    nomination = models.ForeignKey('virtualmeetings.Nomination', on_delete=models.CASCADE,
                                    blank=True, null=True)
-    motion = models.ForeignKey('scipost.Motion', on_delete=models.CASCADE,
+    motion = models.ForeignKey('virtualmeetings.Motion', on_delete=models.CASCADE,
                                blank=True, null=True)
     submission = models.ForeignKey('submissions.Submission',
                                    on_delete=models.CASCADE,
@@ -428,269 +428,218 @@ class PrecookedEmail(models.Model):
         return self.email_subject
 
 
-#############
-# NewsItems #
-#############
-
-class NewsItem(models.Model):
-    date = models.DateField()
-    headline = models.CharField(max_length=300)
-    blurb = models.TextField()
-    followup_link = models.URLField(blank=True, null=True)
-    followup_link_text = models.CharField(max_length=300, blank=True, null=True)
-
-    def __str__(self):
-        return self.date.strftime('%Y-%m-%d') + ', ' + self.headline
-
-    def descriptor_full(self):
-        """ For News page. """
-        descriptor = ('<div class="flex-greybox640">'
-                      '<h3 class="NewsHeadline">{{ headline }}</h3>'
-                      '<p>{{ date }}</p>'
-                      '<p>{{ blurb }}</p>'
-                      )
-        context = Context({'headline': self.headline,
-                           'date': self.date.strftime('%Y-%m-%d'),
-                           'blurb': self.blurb, })
-        if self.followup_link:
-            descriptor += '<p><a href="{{ followup_link }}">{{ followup_link_text }}</a></p>'
-            context['followup_link'] = self.followup_link
-            context['followup_link_text'] = self.followup_link_text
-        descriptor += '</div>'
-        template = Template(descriptor)
-        return template.render(context)
-
-    def descriptor_small(self):
-        """ For index page. """
-        descriptor = ('<h3 class="NewsHeadline">{{ headline }}</h3>'
-                      '<div class="p-2">'
-                      '<p>{{ date }}</p>'
-                      '<p>{{ blurb }}</p>'
-                      )
-        context = Context({'headline': self.headline,
-                           'date': self.date.strftime('%Y-%m-%d'),
-                           'blurb': self.blurb, })
-        if self.followup_link:
-            descriptor += '<p><a href="{{ followup_link }}">{{ followup_link_text }}</a></p>'
-            context['followup_link'] = self.followup_link
-            context['followup_link_text'] = self.followup_link_text
-        descriptor += '</div>'
-        template = Template(descriptor)
-        return template.render(context)
-
-
 #####################################
 # Virtual General Meetings, Motions #
 #####################################
 
-class VGM(models.Model):
-    """
-    Each year, a Virtual General Meeting is held during which operations at
-    SciPost are discussed. A VGM can be attended by Administrators,
-    Advisory Board members and Editorial Fellows.
-    """
-    start_date = models.DateField()
-    end_date = models.DateField()
-    information = models.TextField(default='')
-
-    def __str__(self):
-        return 'From %s to %s' % (self.start_date.strftime('%Y-%m-%d'),
-                                  self.end_date.strftime('%Y-%m-%d'))
-
-
-class Feedback(models.Model):
-    """
-    Feedback, suggestion or criticism on any aspect of SciPost.
-    """
-    VGM = models.ForeignKey(VGM, blank=True, null=True)
-    by = models.ForeignKey(Contributor)
-    date = models.DateField()
-    feedback = models.TextField()
-
-    def __str__(self):
-        return '%s: %s' % (self.by, self.feedback[:50])
-
-    def as_li(self):
-        html = ('<div class="Feedback">'
-                '<h3><em>by {{ by }}</em></h3>'
-                '<p>{{ feedback|linebreaks }}</p>'
-                '</div>')
-        context = Context({
-            'feedback': self.feedback,
-            'by': '%s %s' % (self.by.user.first_name,
-                             self.by.user.last_name)})
-        template = Template(html)
-        return template.render(context)
-
-
-class Nomination(models.Model):
-    """
-    Nomination to an Editorial Fellowship.
-    """
-    VGM = models.ForeignKey(VGM, blank=True, null=True)
-    by = models.ForeignKey(Contributor)
-    date = models.DateField()
-    first_name = models.CharField(max_length=30, default='')
-    last_name = models.CharField(max_length=30, default='')
-    discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES,
-                                  default='physics', verbose_name='Main discipline')
-    expertises = ChoiceArrayField(
-        models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS),
-        blank=True, null=True)
-    webpage = models.URLField(default='')
-    nr_A = models.PositiveIntegerField(default=0)
-    in_agreement = models.ManyToManyField(Contributor,
-                                          related_name='in_agreement_with_nomination', blank=True)
-    nr_N = models.PositiveIntegerField(default=0)
-    in_notsure = models.ManyToManyField(Contributor,
-                                        related_name='in_notsure_with_nomination', blank=True)
-    nr_D = models.PositiveIntegerField(default=0)
-    in_disagreement = models.ManyToManyField(Contributor,
-                                             related_name='in_disagreement_with_nomination',
-                                             blank=True)
-    voting_deadline = models.DateTimeField('voting deadline', default=timezone.now)
-    accepted = models.NullBooleanField()
-
-    def __str__(self):
-        return '%s %s (nominated by %s)' % (self.first_name,
-                                            self.last_name,
-                                            self.by)
-
-    def as_li(self):
-        html = ('<div class="Nomination" id="nomination_id{{ nomination_id }}" '
-                'style="background-color: #eeeeee;">'
-                '<div class="row">'
-                '<div class="col-4">'
-                '<h3><em> {{ name }}</em></h3>'
-                '<p>Nominated by {{ proposer }}</p>'
-                '</div>'
-                '<div class="col-4">'
-                '<p><a href="{{ webpage }}">Webpage</a></p>'
-                '<p>Discipline: {{ discipline }}</p></div>'
-                '<div class="col-4"><p>expertise:<ul>')
-        for exp in self.expertises:
-            html += '<li>%s</li>' % subject_areas_dict[exp]
-        html += '</ul></div></div></div>'
-        context = Context({
-            'nomination_id': self.id,
-            'proposer': '%s %s' % (self.by.user.first_name,
-                                   self.by.user.last_name),
-            'name': self.first_name + ' ' + self.last_name,
-            'discipline': disciplines_dict[self.discipline],
-            'webpage': self.webpage,
-        })
-        template = Template(html)
-        return template.render(context)
-
-    def votes_as_ul(self):
-        template = Template('''
-        <ul class="opinionsDisplay">
-        <li style="background-color: #000099">Agree {{ nr_A }}</li>
-        <li style="background-color: #555555">Abstain {{ nr_N }}</li>
-        <li style="background-color: #990000">Disagree {{ nr_D }}</li>
-        </ul>
-        ''')
-        context = Context({'nr_A': self.nr_A, 'nr_N': self.nr_N, 'nr_D': self.nr_D})
-        return template.render(context)
-
-    def update_votes(self, contributor_id, vote):
-        contributor = get_object_or_404(Contributor, pk=contributor_id)
-        self.in_agreement.remove(contributor)
-        self.in_notsure.remove(contributor)
-        self.in_disagreement.remove(contributor)
-        if vote == 'A':
-            self.in_agreement.add(contributor)
-        elif vote == 'N':
-            self.in_notsure.add(contributor)
-        elif vote == 'D':
-            self.in_disagreement.add(contributor)
-        self.nr_A = self.in_agreement.count()
-        self.nr_N = self.in_notsure.count()
-        self.nr_D = self.in_disagreement.count()
-        self.save()
-
-
-MOTION_CATEGORIES = (
-    ('ByLawAmend', 'Amendments to by-laws'),
-    ('Workflow', 'Editorial workflow improvements'),
-    ('General', 'General'),
-)
-motion_categories_dict = dict(MOTION_CATEGORIES)
-
-
-class Motion(models.Model):
-    """
-    Motion instances are put forward to the Advisory Board and Editorial College
-    and detail suggested changes to rules, procedures etc.
-    They are meant to be voted on at the annual VGM.
-    """
-    category = models.CharField(max_length=10, choices=MOTION_CATEGORIES,
-                                default='General')
-    VGM = models.ForeignKey(VGM, blank=True, null=True)
-    background = models.TextField()
-    motion = models.TextField()
-    put_forward_by = models.ForeignKey(Contributor)
-    date = models.DateField()
-    nr_A = models.PositiveIntegerField(default=0)
-    in_agreement = models.ManyToManyField(Contributor,
-                                          related_name='in_agreement_with_motion', blank=True)
-    nr_N = models.PositiveIntegerField(default=0)
-    in_notsure = models.ManyToManyField(Contributor,
-                                        related_name='in_notsure_with_motion', blank=True)
-    nr_D = models.PositiveIntegerField(default=0)
-    in_disagreement = models.ManyToManyField(Contributor,
-                                             related_name='in_disagreement_with_motion',
-                                             blank=True)
-    voting_deadline = models.DateTimeField('voting deadline', default=timezone.now)
-    accepted = models.NullBooleanField()
-
-    def __str__(self):
-        return self.motion[:32]
-
-    def as_li(self):
-        html = ('<div class="Motion" id="motion_id{{ motion_id }}">'
-                '<h3><em>Motion {{ motion_id }}, put forward by {{ proposer }}</em></h3>'
-                '<h3>Background:</h3><p>{{ background|linebreaks }}</p>'
-                '<h3>Motion:</h3>'
-                '<div class="flex-container"><div class="flex-greybox">'
-                '<p style="background-color: #eeeeee;">{{ motion|linebreaks }}</p>'
-                '</div></div>'
-                '</div>')
-        context = Context({
-            'motion_id': self.id,
-            'proposer': '%s %s' % (self.put_forward_by.user.first_name,
-                                   self.put_forward_by.user.last_name),
-            'background': self.background,
-            'motion': self.motion, })
-        template = Template(html)
-        return template.render(context)
-
-    def votes_as_ul(self):
-        template = Template('''
-        <ul class="opinionsDisplay">
-        <li style="background-color: #000099">Agree {{ nr_A }}</li>
-        <li style="background-color: #555555">Abstain {{ nr_N }}</li>
-        <li style="background-color: #990000">Disagree {{ nr_D }}</li>
-        </ul>
-        ''')
-        context = Context({'nr_A': self.nr_A, 'nr_N': self.nr_N, 'nr_D': self.nr_D})
-        return template.render(context)
-
-    def update_votes(self, contributor_id, vote):
-        contributor = get_object_or_404(Contributor, pk=contributor_id)
-        self.in_agreement.remove(contributor)
-        self.in_notsure.remove(contributor)
-        self.in_disagreement.remove(contributor)
-        if vote == 'A':
-            self.in_agreement.add(contributor)
-        elif vote == 'N':
-            self.in_notsure.add(contributor)
-        elif vote == 'D':
-            self.in_disagreement.add(contributor)
-        self.nr_A = self.in_agreement.count()
-        self.nr_N = self.in_notsure.count()
-        self.nr_D = self.in_disagreement.count()
-        self.save()
+# class VGM(models.Model):
+#     """
+#     Each year, a Virtual General Meeting is held during which operations at
+#     SciPost are discussed. A VGM can be attended by Administrators,
+#     Advisory Board members and Editorial Fellows.
+#     """
+#     start_date = models.DateField()
+#     end_date = models.DateField()
+#     information = models.TextField(default='')
+#
+#     def __str__(self):
+#         return 'From %s to %s' % (self.start_date.strftime('%Y-%m-%d'),
+#                                   self.end_date.strftime('%Y-%m-%d'))
+#
+#
+# class Feedback(models.Model):
+#     """
+#     Feedback, suggestion or criticism on any aspect of SciPost.
+#     """
+#     VGM = models.ForeignKey(VGM, blank=True, null=True)
+#     by = models.ForeignKey(Contributor)
+#     date = models.DateField()
+#     feedback = models.TextField()
+#
+#     def __str__(self):
+#         return '%s: %s' % (self.by, self.feedback[:50])
+#
+#     def as_li(self):
+#         html = ('<div class="Feedback">'
+#                 '<h3><em>by {{ by }}</em></h3>'
+#                 '<p>{{ feedback|linebreaks }}</p>'
+#                 '</div>')
+#         context = Context({
+#             'feedback': self.feedback,
+#             'by': '%s %s' % (self.by.user.first_name,
+#                              self.by.user.last_name)})
+#         template = Template(html)
+#         return template.render(context)
+#
+#
+# class Nomination(models.Model):
+#     """
+#     Nomination to an Editorial Fellowship.
+#     """
+#     VGM = models.ForeignKey(VGM, blank=True, null=True)
+#     by = models.ForeignKey(Contributor)
+#     date = models.DateField()
+#     first_name = models.CharField(max_length=30, default='')
+#     last_name = models.CharField(max_length=30, default='')
+#     discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES,
+#                                   default='physics', verbose_name='Main discipline')
+#     expertises = ChoiceArrayField(
+#         models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS),
+#         blank=True, null=True)
+#     webpage = models.URLField(default='')
+#     nr_A = models.PositiveIntegerField(default=0)
+#     in_agreement = models.ManyToManyField(Contributor,
+#                                           related_name='in_agreement_with_nomination', blank=True)
+#     nr_N = models.PositiveIntegerField(default=0)
+#     in_notsure = models.ManyToManyField(Contributor,
+#                                         related_name='in_notsure_with_nomination', blank=True)
+#     nr_D = models.PositiveIntegerField(default=0)
+#     in_disagreement = models.ManyToManyField(Contributor,
+#                                              related_name='in_disagreement_with_nomination',
+#                                              blank=True)
+#     voting_deadline = models.DateTimeField('voting deadline', default=timezone.now)
+#     accepted = models.NullBooleanField()
+#
+#     def __str__(self):
+#         return '%s %s (nominated by %s)' % (self.first_name,
+#                                             self.last_name,
+#                                             self.by)
+#
+#     def as_li(self):
+#         html = ('<div class="Nomination" id="nomination_id{{ nomination_id }}" '
+#                 'style="background-color: #eeeeee;">'
+#                 '<div class="row">'
+#                 '<div class="col-4">'
+#                 '<h3><em> {{ name }}</em></h3>'
+#                 '<p>Nominated by {{ proposer }}</p>'
+#                 '</div>'
+#                 '<div class="col-4">'
+#                 '<p><a href="{{ webpage }}">Webpage</a></p>'
+#                 '<p>Discipline: {{ discipline }}</p></div>'
+#                 '<div class="col-4"><p>expertise:<ul>')
+#         for exp in self.expertises:
+#             html += '<li>%s</li>' % subject_areas_dict[exp]
+#         html += '</ul></div></div></div>'
+#         context = Context({
+#             'nomination_id': self.id,
+#             'proposer': '%s %s' % (self.by.user.first_name,
+#                                    self.by.user.last_name),
+#             'name': self.first_name + ' ' + self.last_name,
+#             'discipline': disciplines_dict[self.discipline],
+#             'webpage': self.webpage,
+#         })
+#         template = Template(html)
+#         return template.render(context)
+#
+#     def votes_as_ul(self):
+#         template = Template('''
+#         <ul class="opinionsDisplay">
+#         <li style="background-color: #000099">Agree {{ nr_A }}</li>
+#         <li style="background-color: #555555">Abstain {{ nr_N }}</li>
+#         <li style="background-color: #990000">Disagree {{ nr_D }}</li>
+#         </ul>
+#         ''')
+#         context = Context({'nr_A': self.nr_A, 'nr_N': self.nr_N, 'nr_D': self.nr_D})
+#         return template.render(context)
+#
+#     def update_votes(self, contributor_id, vote):
+#         contributor = get_object_or_404(Contributor, pk=contributor_id)
+#         self.in_agreement.remove(contributor)
+#         self.in_notsure.remove(contributor)
+#         self.in_disagreement.remove(contributor)
+#         if vote == 'A':
+#             self.in_agreement.add(contributor)
+#         elif vote == 'N':
+#             self.in_notsure.add(contributor)
+#         elif vote == 'D':
+#             self.in_disagreement.add(contributor)
+#         self.nr_A = self.in_agreement.count()
+#         self.nr_N = self.in_notsure.count()
+#         self.nr_D = self.in_disagreement.count()
+#         self.save()
+#
+#
+# MOTION_CATEGORIES = (
+#     ('ByLawAmend', 'Amendments to by-laws'),
+#     ('Workflow', 'Editorial workflow improvements'),
+#     ('General', 'General'),
+# )
+# motion_categories_dict = dict(MOTION_CATEGORIES)
+#
+#
+# class Motion(models.Model):
+#     """
+#     Motion instances are put forward to the Advisory Board and Editorial College
+#     and detail suggested changes to rules, procedures etc.
+#     They are meant to be voted on at the annual VGM.
+#     """
+#     category = models.CharField(max_length=10, choices=MOTION_CATEGORIES,
+#                                 default='General')
+#     VGM = models.ForeignKey(VGM, blank=True, null=True)
+#     background = models.TextField()
+#     motion = models.TextField()
+#     put_forward_by = models.ForeignKey(Contributor)
+#     date = models.DateField()
+#     nr_A = models.PositiveIntegerField(default=0)
+#     in_agreement = models.ManyToManyField(Contributor,
+#                                           related_name='in_agreement_with_motion', blank=True)
+#     nr_N = models.PositiveIntegerField(default=0)
+#     in_notsure = models.ManyToManyField(Contributor,
+#                                         related_name='in_notsure_with_motion', blank=True)
+#     nr_D = models.PositiveIntegerField(default=0)
+#     in_disagreement = models.ManyToManyField(Contributor,
+#                                              related_name='in_disagreement_with_motion',
+#                                              blank=True)
+#     voting_deadline = models.DateTimeField('voting deadline', default=timezone.now)
+#     accepted = models.NullBooleanField()
+#
+#     def __str__(self):
+#         return self.motion[:32]
+#
+#     def as_li(self):
+#         html = ('<div class="Motion" id="motion_id{{ motion_id }}">'
+#                 '<h3><em>Motion {{ motion_id }}, put forward by {{ proposer }}</em></h3>'
+#                 '<h3>Background:</h3><p>{{ background|linebreaks }}</p>'
+#                 '<h3>Motion:</h3>'
+#                 '<div class="flex-container"><div class="flex-greybox">'
+#                 '<p style="background-color: #eeeeee;">{{ motion|linebreaks }}</p>'
+#                 '</div></div>'
+#                 '</div>')
+#         context = Context({
+#             'motion_id': self.id,
+#             'proposer': '%s %s' % (self.put_forward_by.user.first_name,
+#                                    self.put_forward_by.user.last_name),
+#             'background': self.background,
+#             'motion': self.motion, })
+#         template = Template(html)
+#         return template.render(context)
+#
+#     def votes_as_ul(self):
+#         template = Template('''
+#         <ul class="opinionsDisplay">
+#         <li style="background-color: #000099">Agree {{ nr_A }}</li>
+#         <li style="background-color: #555555">Abstain {{ nr_N }}</li>
+#         <li style="background-color: #990000">Disagree {{ nr_D }}</li>
+#         </ul>
+#         ''')
+#         context = Context({'nr_A': self.nr_A, 'nr_N': self.nr_N, 'nr_D': self.nr_D})
+#         return template.render(context)
+#
+#     def update_votes(self, contributor_id, vote):
+#         contributor = get_object_or_404(Contributor, pk=contributor_id)
+#         self.in_agreement.remove(contributor)
+#         self.in_notsure.remove(contributor)
+#         self.in_disagreement.remove(contributor)
+#         if vote == 'A':
+#             self.in_agreement.add(contributor)
+#         elif vote == 'N':
+#             self.in_notsure.add(contributor)
+#         elif vote == 'D':
+#             self.in_disagreement.add(contributor)
+#         self.nr_A = self.in_agreement.count()
+#         self.nr_N = self.in_notsure.count()
+#         self.nr_D = self.in_disagreement.count()
+#         self.save()
 
 
 #########
diff --git a/scipost/templates/scipost/index.html b/scipost/templates/scipost/index.html
index 8cf87d0cdf9e1ee8e18750bca8be04c57df437d2..13a32d48dfa43b1cd37fb8e0d6af8bb5c5fa36cc 100644
--- a/scipost/templates/scipost/index.html
+++ b/scipost/templates/scipost/index.html
@@ -9,7 +9,7 @@
         {% if latest_newsitems %}
         <div class="col-md-6 {% if user.is_authenticated %}col-lg-4{% else %}col-lg-3{% endif %}">
             <div class="panel">
-                <h1><a href="{% url 'scipost:news' %}">News</a><a style="float: right;" href="{% url 'scipost:feeds' %}"><img src="{% static 'scipost/images/feed-icon-14x14.png' %}" alt="Feed logo" width="14"></a></h1>
+                <h1><a href="{% url 'news:news' %}">News</a><a style="float: right;" href="{% url 'scipost:feeds' %}"><img src="{% static 'scipost/images/feed-icon-14x14.png' %}" alt="Feed logo" width="14"></a></h1>
                 <p>Latest news and announcements.</p>
                 {# <hr class="hr6"/>#}
                 <ul class="NewsItemsList">
diff --git a/scipost/urls.py b/scipost/urls.py
index e3b8c573147bbafd6e2541cce29bbeee3d68279e..1ed0f1b6a55fccc4f199d49d5b767102ec62833b 100644
--- a/scipost/urls.py
+++ b/scipost/urls.py
@@ -17,7 +17,6 @@ urlpatterns = [
         name='acknowledgement'),
 
     ## Info
-    url(r'^news$', views.news, name='news'),
     url(r'^about$', TemplateView.as_view(template_name='scipost/about.html'), name='about'),
     url(r'^call$', TemplateView.as_view(template_name='scipost/call.html'), name='call'),
     url(r'^foundation$', TemplateView.as_view(template_name='scipost/foundation.html'),
diff --git a/scipost/views.py b/scipost/views.py
index 78635541dc0746e2cb539ba7308640f21686acfa..496ceb09a3032657e7148518705a682a1aa36d56 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -27,11 +27,10 @@ from guardian.shortcuts import assign_perm
 
 from .constants import SCIPOST_SUBJECT_AREAS
 from .models import Contributor, CitationNotification, UnavailabilityPeriod,\
-                    DraftInvitation, RegistrationInvitation, NewsItem,\
+                    DraftInvitation, RegistrationInvitation, Remark,\
                     List, Team, Graph, Node, Arc,\
                     title_dict, SciPost_from_addresses_dict,\
-                    AuthorshipClaim, SupportingPartner, SPBMembershipAgreement,\
-                    VGM, Feedback, Nomination, Remark, Motion, motion_categories_dict
+                    AuthorshipClaim, SupportingPartner, SPBMembershipAgreement
 from .forms import AuthenticationForm, DraftInvitationForm, UnavailabilityPeriodForm,\
                    RegistrationForm, RegistrationInvitationForm, AuthorshipClaimForm,\
                    ModifyPersonalMessageForm, SearchForm, VetRegistrationForm, reg_ref_dict,\
@@ -48,12 +47,15 @@ from commentaries.models import Commentary
 from commentaries.forms import CommentarySearchForm
 from comments.models import Comment
 from journals.models import Publication
+from news.models import NewsItem
 from submissions.models import SUBMISSION_STATUS_PUBLICLY_UNLISTED
 from submissions.models import Submission, EditorialAssignment
 from submissions.models import RefereeInvitation, Report, EICRecommendation
 from submissions.forms import SubmissionSearchForm
 from theses.models import ThesisLink
 from theses.forms import ThesisLinkSearchForm
+from virtualmeetings.models import VGM, Feedback, Nomination, Motion
+from virtualmeetings.constants import motion_categories_dict
 
 
 ##############
@@ -232,12 +234,6 @@ def base(request):
     return render(request, 'scipost/base.html')
 
 
-def news(request):
-    newsitems = NewsItem.objects.all().order_by('-date')
-    context = {'newsitems': newsitems}
-    return render(request, 'scipost/news.html', context)
-
-
 def feeds(request):
     context = {'subject_areas_physics': SCIPOST_SUBJECT_AREAS[0][1]}
     return render(request, 'scipost/feeds.html', context)
diff --git a/virtualmeetings/__init__.py b/virtualmeetings/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/virtualmeetings/admin.py b/virtualmeetings/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..c831a447b58b6d95edd086fda5750bbfc5e315c6
--- /dev/null
+++ b/virtualmeetings/admin.py
@@ -0,0 +1,31 @@
+from django.contrib import admin
+
+from .models import VGM, Feedback, Nomination, Motion
+
+
+class VGMAdmin(admin.ModelAdmin):
+    search_fields = ['start_date']
+
+
+admin.site.register(VGM, VGMAdmin)
+
+
+class FeedbackAdmin(admin.ModelAdmin):
+    search_fields = ['feedback', 'by']
+
+
+admin.site.register(Feedback, FeedbackAdmin)
+
+
+class NominationAdmin(admin.ModelAdmin):
+    search_fields = ['last_name', 'first_name', 'by']
+
+
+admin.site.register(Nomination, NominationAdmin)
+
+
+class MotionAdmin(admin.ModelAdmin):
+    search_fields = ['background', 'motion', 'put_forward_by']
+
+
+admin.site.register(Motion, MotionAdmin)
diff --git a/virtualmeetings/apps.py b/virtualmeetings/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..9bbfd4ea04576759b428cbd1b1127a2767b82216
--- /dev/null
+++ b/virtualmeetings/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class VirtualmeetingsConfig(AppConfig):
+    name = 'virtualmeetings'
diff --git a/virtualmeetings/constants.py b/virtualmeetings/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..f75281f3af9bd47307eb81ce6542ae3b89b8ffc8
--- /dev/null
+++ b/virtualmeetings/constants.py
@@ -0,0 +1,9 @@
+MOTION_AMENDMENTS = 'ByLawAmend'
+MOTION_WORKFLOW = 'Workflow'
+MOTION_GENERAL = 'General'
+MOTION_CATEGORIES = (
+    (MOTION_AMENDMENTS, 'Amendments to by-laws'),
+    (MOTION_WORKFLOW, 'Editorial workflow improvements'),
+    (MOTION_GENERAL, 'General'),
+)
+motion_categories_dict = dict(MOTION_CATEGORIES)
diff --git a/virtualmeetings/forms.py b/virtualmeetings/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..54fb4a68e81dfe0f7a1f54be8405a5ae5cfe60e4
--- /dev/null
+++ b/virtualmeetings/forms.py
@@ -0,0 +1,61 @@
+from django import forms
+
+from crispy_forms.helper import FormHelper
+from crispy_forms.layout import Layout, Div, Field, HTML, Submit
+
+from .models import Feedback, Nomination, Motion
+
+from scipost.constants import SCIPOST_SUBJECT_AREAS
+
+
+class FeedbackForm(forms.ModelForm):
+    class Meta:
+        model = Feedback
+        fields = ['feedback']
+
+
+class NominationForm(forms.ModelForm):
+    class Meta:
+        model = Nomination
+        fields = ['first_name', 'last_name',
+                  'discipline', 'expertises', 'webpage']
+
+    def __init__(self, *args, **kwargs):
+        super(NominationForm, self).__init__(*args, **kwargs)
+        self.fields['expertises'].widget = forms.SelectMultiple(choices=SCIPOST_SUBJECT_AREAS)
+
+
+class MotionForm(forms.ModelForm):
+    class Meta:
+        model = Motion
+        fields = ['category', 'background', 'motion']
+
+    def __init__(self, *args, **kwargs):
+        super(MotionForm, self).__init__(*args, **kwargs)
+        self.fields['background'].label = ''
+        self.fields['background'].widget.attrs.update(
+            {'rows': 8, 'cols': 100,
+             'placeholder': 'Provide useful background information on your Motion.'})
+        self.fields['motion'].label = ''
+        self.fields['motion'].widget.attrs.update(
+            {'rows': 8, 'cols': 100,
+             'placeholder': 'Phrase your Motion as clearly and succinctly as possible.'})
+        self.helper = FormHelper()
+        self.helper.layout = Layout(
+            Field('category'),
+            Div(
+                Div(HTML('<p>Background:</p>'),
+                    css_class="col-2"),
+                Div(
+                    Field('background'),
+                    css_class="col-10"),
+                css_class="row"),
+            Div(
+                Div(HTML('<p>Motion:</p>'),
+                    css_class="col-2"),
+                Div(
+                    Field('motion'),
+                    css_class="col-10"),
+                css_class="row"),
+            Submit('submit', 'Submit'),
+        )
diff --git a/virtualmeetings/migrations/0001_initial.py b/virtualmeetings/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..567764e57de6f7ee69c5fa158016d95f64917c0a
--- /dev/null
+++ b/virtualmeetings/migrations/0001_initial.py
@@ -0,0 +1,145 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-03-06 07:04
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import scipost.models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('scipost', '0039_auto_20170306_0804'),
+    ]
+
+    state_operations = [
+        migrations.CreateModel(
+            name='Feedback',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('date', models.DateField()),
+                ('feedback', models.TextField()),
+            ],
+            options={
+                'db_table': 'scipost_feedback',
+            },
+        ),
+        migrations.CreateModel(
+            name='Motion',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('category', models.CharField(choices=[('ByLawAmend', 'Amendments to by-laws'), ('Workflow', 'Editorial workflow improvements'), ('General', 'General')], default='General', max_length=10)),
+                ('background', models.TextField()),
+                ('motion', models.TextField()),
+                ('date', models.DateField()),
+                ('nr_A', models.PositiveIntegerField(default=0)),
+                ('nr_N', models.PositiveIntegerField(default=0)),
+                ('nr_D', models.PositiveIntegerField(default=0)),
+                ('voting_deadline', models.DateTimeField(default=django.utils.timezone.now, verbose_name='voting deadline')),
+                ('accepted', models.NullBooleanField()),
+            ],
+            options={
+                'db_table': 'scipost_motion',
+            },
+        ),
+        migrations.CreateModel(
+            name='Nomination',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('date', models.DateField()),
+                ('first_name', models.CharField(default='', max_length=30)),
+                ('last_name', models.CharField(default='', max_length=30)),
+                ('discipline', models.CharField(choices=[('physics', 'Physics'), ('astrophysics', 'Astrophysics'), ('mathematics', 'Mathematics'), ('computerscience', 'Computer Science')], default='physics', max_length=20, verbose_name='Main discipline')),
+                ('expertises', scipost.models.ChoiceArrayField(base_field=models.CharField(choices=[('Physics', (('Phys:AE', 'Atomic, Molecular and Optical Physics - Experiment'), ('Phys:AT', 'Atomic, Molecular and Optical Physics - Theory'), ('Phys:BI', 'Biophysics'), ('Phys:CE', 'Condensed Matter Physics - Experiment'), ('Phys:CT', 'Condensed Matter Physics - Theory'), ('Phys:FD', 'Fluid Dynamics'), ('Phys:GR', 'Gravitation, Cosmology and Astroparticle Physics'), ('Phys:HE', 'High-Energy Physics - Experiment'), ('Phys:HT', 'High-Energy Physics- Theory'), ('Phys:HP', 'High-Energy Physics - Phenomenology'), ('Phys:MP', 'Mathematical Physics'), ('Phys:NE', 'Nuclear Physics - Experiment'), ('Phys:NT', 'Nuclear Physics - Theory'), ('Phys:QP', 'Quantum Physics'), ('Phys:SM', 'Statistical and Soft Matter Physics'))), ('Astrophysics', (('Astro:GA', 'Astrophysics of Galaxies'), ('Astro:CO', 'Cosmology and Nongalactic Astrophysics'), ('Astro:EP', 'Earth and Planetary Astrophysics'), ('Astro:HE', 'High Energy Astrophysical Phenomena'), ('Astro:IM', 'Instrumentation and Methods for Astrophysics'), ('Astro:SR', 'Solar and Stellar Astrophysics'))), ('Mathematics', (('Math:AG', 'Algebraic Geometry'), ('Math:AT', 'Algebraic Topology'), ('Math:AP', 'Analysis of PDEs'), ('Math:CT', 'Category Theory'), ('Math:CA', 'Classical Analysis and ODEs'), ('Math:CO', 'Combinatorics'), ('Math:AC', 'Commutative Algebra'), ('Math:CV', 'Complex Variables'), ('Math:DG', 'Differential Geometry'), ('Math:DS', 'Dynamical Systems'), ('Math:FA', 'Functional Analysis'), ('Math:GM', 'General Mathematics'), ('Math:GN', 'General Topology'), ('Math:GT', 'Geometric Topology'), ('Math:GR', 'Group Theory'), ('Math:HO', 'History and Overview'), ('Math:IT', 'Information Theory'), ('Math:KT', 'K-Theory and Homology'), ('Math:LO', 'Logic'), ('Math:MP', 'Mathematical Physics'), ('Math:MG', 'Metric Geometry'), ('Math:NT', 'Number Theory'), ('Math:NA', 'Numerical Analysis'), ('Math:OA', 'Operator Algebras'), ('Math:OC', 'Optimization and Control'), ('Math:PR', 'Probability'), ('Math:QA', 'Quantum Algebra'), ('Math:RT', 'Representation Theory'), ('Math:RA', 'Rings and Algebras'), ('Math:SP', 'Spectral Theory'), ('Math:ST', 'Statistics Theory'), ('Math:SG', 'Symplectic Geometry'))), ('Computer Science', (('Comp:AI', 'Artificial Intelligence'), ('Comp:CC', 'Computational Complexity'), ('Comp:CE', 'Computational Engineering, Finance, and Science'), ('Comp:CG', 'Computational Geometry'), ('Comp:GT', 'Computer Science and Game Theory'), ('Comp:CV', 'Computer Vision and Pattern Recognition'), ('Comp:CY', 'Computers and Society'), ('Comp:CR', 'Cryptography and Security'), ('Comp:DS', 'Data Structures and Algorithms'), ('Comp:DB', 'Databases'), ('Comp:DL', 'Digital Libraries'), ('Comp:DM', 'Discrete Mathematics'), ('Comp:DC', 'Distributed, Parallel, and Cluster Computing'), ('Comp:ET', 'Emerging Technologies'), ('Comp:FL', 'Formal Languages and Automata Theory'), ('Comp:GL', 'General Literature'), ('Comp:GR', 'Graphics'), ('Comp:AR', 'Hardware Architecture'), ('Comp:HC', 'Human-Computer Interaction'), ('Comp:IR', 'Information Retrieval'), ('Comp:IT', 'Information Theory'), ('Comp:LG', 'Learning'), ('Comp:LO', 'Logic in Computer Science'), ('Comp:MS', 'Mathematical Software'), ('Comp:MA', 'Multiagent Systems'), ('Comp:MM', 'Multimedia'), ('Comp:NI', 'Networking and Internet Architecture'), ('Comp:NE', 'Neural and Evolutionary Computing'), ('Comp:NA', 'Numerical Analysis'), ('Comp:OS', 'Operating Systems'), ('Comp:OH', 'Other Computer Science'), ('Comp:PF', 'Performance'), ('Comp:PL', 'Programming Languages'), ('Comp:RO', 'Robotics'), ('Comp:SI', 'Social and Information Networks'), ('Comp:SE', 'Software Engineering'), ('Comp:SD', 'Sound'), ('Comp:SC', 'Symbolic Computation'), ('Comp:SY', 'Systems and Control')))], max_length=10), blank=True, null=True, size=None)),
+                ('webpage', models.URLField(default='')),
+                ('nr_A', models.PositiveIntegerField(default=0)),
+                ('nr_N', models.PositiveIntegerField(default=0)),
+                ('nr_D', models.PositiveIntegerField(default=0)),
+                ('voting_deadline', models.DateTimeField(default=django.utils.timezone.now, verbose_name='voting deadline')),
+                ('accepted', models.NullBooleanField()),
+            ],
+            options={
+                'db_table': 'scipost_nomination',
+            },
+        ),
+        migrations.CreateModel(
+            name='VGM',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('start_date', models.DateField()),
+                ('end_date', models.DateField()),
+                ('information', models.TextField(default='')),
+            ],
+            options={
+                'db_table': 'scipost_vgm',
+            },
+        ),
+        migrations.AddField(
+            model_name='nomination',
+            name='VGM',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.VGM'),
+        ),
+        migrations.AddField(
+            model_name='nomination',
+            name='by',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor'),
+        ),
+        migrations.AddField(
+            model_name='nomination',
+            name='in_agreement',
+            field=models.ManyToManyField(blank=True, related_name='in_agreement_with_nomination', to='scipost.Contributor'),
+        ),
+        migrations.AddField(
+            model_name='nomination',
+            name='in_disagreement',
+            field=models.ManyToManyField(blank=True, related_name='in_disagreement_with_nomination', to='scipost.Contributor'),
+        ),
+        migrations.AddField(
+            model_name='nomination',
+            name='in_notsure',
+            field=models.ManyToManyField(blank=True, related_name='in_notsure_with_nomination', to='scipost.Contributor'),
+        ),
+        migrations.AddField(
+            model_name='motion',
+            name='VGM',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.VGM'),
+        ),
+        migrations.AddField(
+            model_name='motion',
+            name='in_agreement',
+            field=models.ManyToManyField(blank=True, related_name='in_agreement_with_motion', to='scipost.Contributor'),
+        ),
+        migrations.AddField(
+            model_name='motion',
+            name='in_disagreement',
+            field=models.ManyToManyField(blank=True, related_name='in_disagreement_with_motion', to='scipost.Contributor'),
+        ),
+        migrations.AddField(
+            model_name='motion',
+            name='in_notsure',
+            field=models.ManyToManyField(blank=True, related_name='in_notsure_with_motion', to='scipost.Contributor'),
+        ),
+        migrations.AddField(
+            model_name='motion',
+            name='put_forward_by',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor'),
+        ),
+        migrations.AddField(
+            model_name='feedback',
+            name='VGM',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='virtualmeetings.VGM'),
+        ),
+        migrations.AddField(
+            model_name='feedback',
+            name='by',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor'),
+        ),
+    ]
+
+    operations = [
+        migrations.SeparateDatabaseAndState(state_operations=state_operations)
+    ]
diff --git a/virtualmeetings/migrations/__init__.py b/virtualmeetings/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/virtualmeetings/models.py b/virtualmeetings/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..55aaacefe039c513e6f0a63644e44d2d4f59c46c
--- /dev/null
+++ b/virtualmeetings/models.py
@@ -0,0 +1,223 @@
+from django.db import models
+from django.shortcuts import get_object_or_404
+from django.template import Context, Template
+from django.utils import timezone
+
+from .constants import MOTION_CATEGORIES
+
+from scipost.constants import SCIPOST_DISCIPLINES, SCIPOST_SUBJECT_AREAS,\
+                              subject_areas_dict, disciplines_dict
+from scipost.models import Contributor, ChoiceArrayField
+
+
+class VGM(models.Model):
+    """
+    Each year, a Virtual General Meeting is held during which operations at
+    SciPost are discussed. A VGM can be attended by Administrators,
+    Advisory Board members and Editorial Fellows.
+    """
+    start_date = models.DateField()
+    end_date = models.DateField()
+    information = models.TextField(default='')
+
+    class Meta:
+        db_table = 'scipost_vgm'
+
+    def __str__(self):
+        return 'From %s to %s' % (self.start_date.strftime('%Y-%m-%d'),
+                                  self.end_date.strftime('%Y-%m-%d'))
+
+
+class Feedback(models.Model):
+    """
+    Feedback, suggestion or criticism on any aspect of SciPost.
+    """
+    VGM = models.ForeignKey(VGM, blank=True, null=True)
+    by = models.ForeignKey(Contributor)
+    date = models.DateField()
+    feedback = models.TextField()
+
+    class Meta:
+        db_table = 'scipost_feedback'
+
+    def __str__(self):
+        return '%s: %s' % (self.by, self.feedback[:50])
+
+    def as_li(self):
+        html = ('<div class="Feedback">'
+                '<h3><em>by {{ by }}</em></h3>'
+                '<p>{{ feedback|linebreaks }}</p>'
+                '</div>')
+        context = Context({
+            'feedback': self.feedback,
+            'by': '%s %s' % (self.by.user.first_name,
+                             self.by.user.last_name)})
+        template = Template(html)
+        return template.render(context)
+
+
+class Nomination(models.Model):
+    """
+    Nomination to an Editorial Fellowship.
+    """
+    VGM = models.ForeignKey(VGM, blank=True, null=True)
+    by = models.ForeignKey(Contributor)
+    date = models.DateField()
+    first_name = models.CharField(max_length=30, default='')
+    last_name = models.CharField(max_length=30, default='')
+    discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES,
+                                  default='physics', verbose_name='Main discipline')
+    expertises = ChoiceArrayField(
+        models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS),
+        blank=True, null=True)
+    webpage = models.URLField(default='')
+    nr_A = models.PositiveIntegerField(default=0)
+    in_agreement = models.ManyToManyField(Contributor,
+                                          related_name='in_agreement_with_nomination', blank=True)
+    nr_N = models.PositiveIntegerField(default=0)
+    in_notsure = models.ManyToManyField(Contributor,
+                                        related_name='in_notsure_with_nomination', blank=True)
+    nr_D = models.PositiveIntegerField(default=0)
+    in_disagreement = models.ManyToManyField(Contributor,
+                                             related_name='in_disagreement_with_nomination',
+                                             blank=True)
+    voting_deadline = models.DateTimeField('voting deadline', default=timezone.now)
+    accepted = models.NullBooleanField()
+
+    class Meta:
+        db_table = 'scipost_nomination'
+
+    def __str__(self):
+        return '%s %s (nominated by %s)' % (self.first_name,
+                                            self.last_name,
+                                            self.by)
+
+    def as_li(self):
+        html = ('<div class="Nomination" id="nomination_id{{ nomination_id }}" '
+                'style="background-color: #eeeeee;">'
+                '<div class="row">'
+                '<div class="col-4">'
+                '<h3><em> {{ name }}</em></h3>'
+                '<p>Nominated by {{ proposer }}</p>'
+                '</div>'
+                '<div class="col-4">'
+                '<p><a href="{{ webpage }}">Webpage</a></p>'
+                '<p>Discipline: {{ discipline }}</p></div>'
+                '<div class="col-4"><p>expertise:<ul>')
+        for exp in self.expertises:
+            html += '<li>%s</li>' % subject_areas_dict[exp]
+        html += '</ul></div></div></div>'
+        context = Context({
+            'nomination_id': self.id,
+            'proposer': '%s %s' % (self.by.user.first_name,
+                                   self.by.user.last_name),
+            'name': self.first_name + ' ' + self.last_name,
+            'discipline': disciplines_dict[self.discipline],
+            'webpage': self.webpage,
+        })
+        template = Template(html)
+        return template.render(context)
+
+    def votes_as_ul(self):
+        template = Template('''
+        <ul class="opinionsDisplay">
+        <li style="background-color: #000099">Agree {{ nr_A }}</li>
+        <li style="background-color: #555555">Abstain {{ nr_N }}</li>
+        <li style="background-color: #990000">Disagree {{ nr_D }}</li>
+        </ul>
+        ''')
+        context = Context({'nr_A': self.nr_A, 'nr_N': self.nr_N, 'nr_D': self.nr_D})
+        return template.render(context)
+
+    def update_votes(self, contributor_id, vote):
+        contributor = get_object_or_404(Contributor, pk=contributor_id)
+        self.in_agreement.remove(contributor)
+        self.in_notsure.remove(contributor)
+        self.in_disagreement.remove(contributor)
+        if vote == 'A':
+            self.in_agreement.add(contributor)
+        elif vote == 'N':
+            self.in_notsure.add(contributor)
+        elif vote == 'D':
+            self.in_disagreement.add(contributor)
+        self.nr_A = self.in_agreement.count()
+        self.nr_N = self.in_notsure.count()
+        self.nr_D = self.in_disagreement.count()
+        self.save()
+
+
+class Motion(models.Model):
+    """
+    Motion instances are put forward to the Advisory Board and Editorial College
+    and detail suggested changes to rules, procedures etc.
+    They are meant to be voted on at the annual VGM.
+    """
+    category = models.CharField(max_length=10, choices=MOTION_CATEGORIES, default='General')
+    VGM = models.ForeignKey(VGM, blank=True, null=True)
+    background = models.TextField()
+    motion = models.TextField()
+    put_forward_by = models.ForeignKey(Contributor)
+    date = models.DateField()
+    nr_A = models.PositiveIntegerField(default=0)
+    in_agreement = models.ManyToManyField(Contributor,
+                                          related_name='in_agreement_with_motion', blank=True)
+    nr_N = models.PositiveIntegerField(default=0)
+    in_notsure = models.ManyToManyField(Contributor,
+                                        related_name='in_notsure_with_motion', blank=True)
+    nr_D = models.PositiveIntegerField(default=0)
+    in_disagreement = models.ManyToManyField(Contributor,
+                                             related_name='in_disagreement_with_motion',
+                                             blank=True)
+    voting_deadline = models.DateTimeField('voting deadline', default=timezone.now)
+    accepted = models.NullBooleanField()
+
+    class Meta:
+        db_table = 'scipost_motion'
+
+    def __str__(self):
+        return self.motion[:32]
+
+    def as_li(self):
+        html = ('<div class="Motion" id="motion_id{{ motion_id }}">'
+                '<h3><em>Motion {{ motion_id }}, put forward by {{ proposer }}</em></h3>'
+                '<h3>Background:</h3><p>{{ background|linebreaks }}</p>'
+                '<h3>Motion:</h3>'
+                '<div class="flex-container"><div class="flex-greybox">'
+                '<p style="background-color: #eeeeee;">{{ motion|linebreaks }}</p>'
+                '</div></div>'
+                '</div>')
+        context = Context({
+            'motion_id': self.id,
+            'proposer': '%s %s' % (self.put_forward_by.user.first_name,
+                                   self.put_forward_by.user.last_name),
+            'background': self.background,
+            'motion': self.motion, })
+        template = Template(html)
+        return template.render(context)
+
+    def votes_as_ul(self):
+        template = Template('''
+        <ul class="opinionsDisplay">
+        <li style="background-color: #000099">Agree {{ nr_A }}</li>
+        <li style="background-color: #555555">Abstain {{ nr_N }}</li>
+        <li style="background-color: #990000">Disagree {{ nr_D }}</li>
+        </ul>
+        ''')
+        context = Context({'nr_A': self.nr_A, 'nr_N': self.nr_N, 'nr_D': self.nr_D})
+        return template.render(context)
+
+    def update_votes(self, contributor_id, vote):
+        contributor = get_object_or_404(Contributor, pk=contributor_id)
+        self.in_agreement.remove(contributor)
+        self.in_notsure.remove(contributor)
+        self.in_disagreement.remove(contributor)
+        if vote == 'A':
+            self.in_agreement.add(contributor)
+        elif vote == 'N':
+            self.in_notsure.add(contributor)
+        elif vote == 'D':
+            self.in_disagreement.add(contributor)
+        self.nr_A = self.in_agreement.count()
+        self.nr_N = self.in_notsure.count()
+        self.nr_D = self.in_disagreement.count()
+        self.save()
diff --git a/virtualmeetings/templates/virtualmeetings/VGM_detail.html b/virtualmeetings/templates/virtualmeetings/VGM_detail.html
new file mode 100644
index 0000000000000000000000000000000000000000..0420363c16e093409a430ceee2b8d259bc6f9dd2
--- /dev/null
+++ b/virtualmeetings/templates/virtualmeetings/VGM_detail.html
@@ -0,0 +1,352 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: VGM detail{% endblock pagetitle %}
+
+{% load staticfiles %}
+
+{% block bodysup %}
+
+<script>
+$(document).ready(function(){
+
+  $("#submitFeedbackForm").hide();
+  $("#submitFeedbackButton").click( function() {
+     $(this).next("form").toggle();
+  });
+
+  $("#FellowshipListing").hide();
+  $("#FellowshipListingButton").click( function() {
+      $("#FellowshipListing").toggle();
+  });
+
+  $("#submitNominationForm").hide();
+  $("#submitNominationButton").click( function() {
+     $(this).next("form").toggle();
+  });
+
+  $("#submitMotionForm").hide();
+  $("#submitMotionButton").click( function() {
+     $(this).next("form").toggle();
+  });
+
+  $(".submitRemarkForm").hide();
+
+  $(".submitRemarkButton").click( function() {
+     $(this).next("div").toggle();
+  });
+  });
+
+</script>
+
+<section>
+  <div class="flex-container">
+    <div class="flex-greybox">
+      <h1>SciPost Virtual General Meeting</h1>
+    </div>
+  </div>
+  <div class="flex-container">
+    <div class="flex-whitebox">
+      <h2>On this page:</h2>
+      <ul>
+	<li><a href="#Information">Information message</a></li>
+	<li><a href="#Feedback">Feedback</a></li>
+	<li><a href="#Nominations">Nominations</a></li>
+	<li><a href="#Motions">Motions</a></li>
+      </ul>
+    </div>
+  </div>
+  <hr class="hr12"/>
+</section>
+
+
+<section id="Information">
+  <div class="flex-container">
+    <div class="flex-greybox">
+      <h2>Information message from SciPost Administration</h2>
+    </div>
+  </div>
+  <div class="flex-whitebox">
+    {{ VGM_information }}
+  </div>
+  <br/>
+  <div class="flex-whitebox">
+    <h3>Quick bullet points:</h3>
+    <ul>
+      <li>This VGM is scheduled from {{ VGM.start_date|date:'Y-m-d' }} to {{ VGM.end_date|date:'Y-m-d' }}.</li>
+      <li>Your feedback/suggestions/criticisms on any aspect of SciPost are greatly valued. Provide them by filling the <a href="#FeedbackBox">feedback form</a>.</li>
+      <li>Your nominations to the Editorial College are welcome. Simply fill the <a href="#NominationBox">nomination form</a>, and cast your vote on current nominations.</li>
+      <li>For substantial changes, for example to the by-laws, new Motions can be put forward until the end of the meeting using the <a href="#MotionBox">form</a>.</li>
+      <li>Voting on Motions is open until one week after the meeting.</li>
+      <li>You a referred to the <a href="{% url 'scipost:EdCol_by-laws' %}">by-laws</a>, section 2 for further details about the procedures.</li>
+    </ul>
+  </div>
+  <br/>
+  <hr class="hr12"/>
+</section>
+
+<section id="Feedback">
+  <div class="flex-container">
+    <div class="flex-greybox" id="FeedbackBox">
+      <h2>Feedback on SciPost</h2>
+      <button id="submitFeedbackButton">Provide feedback</button>
+      <form id="submitFeedbackForm" action="{% url 'virtualmeetings:feedback' VGM_id=VGM.id %}" method="post">
+	{% csrf_token %}
+	{{ feedback_form.as_p }}
+	<input type="submit" value="Submit"/>
+      </form>
+    </div>
+  </div>
+  <div class="flex-container">
+    <div class="flex-greybox">
+      <h2>General Feedback provided</h2>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col-1"></div>
+    <div class="col-10">
+      <ul>
+	{% for feedback in feedback_received %}
+	<li>{{ feedback.as_li }}</li>
+	<button class="submitRemarkButton" id="remarkButton{{ nomination.id }}">Add a remark on this Feedback</button>
+	<div class="submitRemarkForm" id="remarkForm{{ feedback.id }}">
+	  <form action="{% url 'virtualmeetings:add_remark_on_feedback' VGM_id=VGM.id feedback_id=feedback.id %}" method="post">
+	    {% csrf_token %}
+	    {{ remark_form.as_p }}
+	    <input type="submit" value="Submit" />
+	  </form>
+	</div>
+	{% if feedback.remark_set.all %}
+	<h3>Remarks on this feedback:</h3>
+	<ul>
+	  {% for rem in feedback.remark_set.all %}
+	  {{ rem.as_li }}
+	  {% endfor %}
+	</ul>
+	{% endif %}
+	{% endfor %}
+      </ul>
+    </div>
+  </div>
+  <hr class="hr12"/>
+</section>
+
+<section id="Nominations">
+  <div class="flex-container">
+    <div class="flex-greybox" id="NominationBox">
+      <h2>Nominations to the Editorial College</h2>
+      <button id="submitNominationButton">Nominate an Editorial Fellow candidate</button>
+      <form id="submitNominationForm" action="{% url 'virtualmeetings:nominate_Fellow' VGM_id=VGM.id %}" method="post">
+	{% csrf_token %}
+	{{ nomination_form.as_p }}
+        <input type="submit" value="Submit"/>
+      </form>
+    </div>
+  </div>
+  <button id="FellowshipListingButton">View/hide Fellows and Invitations listings</button>
+  <div class="row" id="FellowshipListing">
+    <div class="col-6">
+      <div class="flex-container">
+	<div class="flex-greybox">
+	  <h3>Current Fellows</h3>
+	</div>
+      </div>
+      <div class="flex-container">
+	<div class="flex-whitebox">
+	  <table class="tableofInviteesResponded">
+	    {% for Fellow in current_Fellows %}
+	    <tr><td>{{ Fellow }}</td><td>{{ Fellow.discipline_as_string }}</td>
+	      <td>{{ Fellow.expertises_as_string }}</td></tr>
+	    {% endfor %}
+	  </table>
+	</div>
+      </div>
+    </div>
+    <div class="col-6">
+      <div class="flex-container">
+	<div class="flex-greybox">
+	  <h3>Invitations currently outstanding</h3>
+	</div>
+      </div>
+      <div class="flex-container">
+	<div class="flex-whitebox">
+	  <table class="tableofInvitees">
+	    {% for invitee in pending_inv_Fellows %}
+	    <tr><td>{{ invitee.first_name }} {{ invitee.last_name }}</td></tr>
+	    {% endfor %}
+	  </table>
+	</div>
+      </div>
+      <div class="flex-container">
+	<div class="flex-greybox">
+	  <h3>Invitations which have been turned down</h3>
+	</div>
+      </div>
+      <div class="flex-container">
+	<div class="flex-whitebox">
+	  <table class="tableofInviteesDeclined">
+	    {% for invitee in declined_inv_Fellows %}
+	    <tr><td>{{ invitee.first_name }} {{ invitee.last_name }}</td></tr>
+	    {% endfor %}
+	  </table>
+	</div>
+      </div>
+    </div>
+  </div>
+
+  {% if nominations %}
+  <div class="row">
+    <div class="flex-container">
+      <div class="flex-greybox">
+	<h2>Nominations under consideration</h2>
+      </div>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col-1"></div>
+    <div class="col-10">
+    <ul style="list-style-type: none;">
+      {% for nomination in nominations %}
+      <li>
+	{{ nomination.as_li }}
+	<br/>
+	<div class="opinionsDisplay">
+	  <h4>Your opinion on this Nomination (voting deadline: {{ nomination.voting_deadline|date:'y-m-d' }}):</h4>
+	  <form action="{% url 'virtualmeetings:vote_on_nomination' nomination_id=nomination.id vote='A' %}" method="post">
+            {% csrf_token %}
+            <input type="submit" class="agree" value="Agree {{ nomination.nr_A }} "/>
+	  </form>
+	  <form action="{% url 'virtualmeetings:vote_on_nomination' nomination_id=nomination.id vote='N' %}" method="post">
+            {% csrf_token %}
+            <input type="submit" class="notsure" value="Not sure {{ nomination.nr_N }}"/>
+	  </form>
+	  <form action="{% url 'virtualmeetings:vote_on_nomination' nomination_id=nomination.id vote='D'%}" method="post">
+            {% csrf_token %}
+            <input type="submit" class="disagree" value="Disagree {{ nomination.nr_D }}"/>
+	  </form>
+	  {% if request.user.contributor in nomination.in_agreement.all %}
+	  <strong>(you have voted: Agreed)</strong>
+	  {% elif request.user.contributor in nomination.in_notsure.all %}
+	  <strong>(you have voted: Not sure)</strong>
+	  {% elif request.user.contributor in nomination.in_disagreement.all %}
+	  <strong>(you have voted: Disagree)</strong>
+	  {% endif %}
+	</div>
+	<br/><br/>
+	<button class="submitRemarkButton" id="remarkButton{{ nomination.id }}">Add a remark on this Nomination</button>
+	<div class="submitRemarkForm" id="remarkForm{{ nomination.id }}">
+	  <form action="{% url 'virtualmeetings:add_remark_on_nomination' VGM_id=VGM.id nomination_id=nomination.id %}" method="post">
+	    {% csrf_token %}
+	    {{ remark_form.as_p }}
+	    <input type="submit" value="Submit" />
+	  </form>
+	</div>
+	{% if nomination.remark_set.all %}
+	<h3>Remarks on this nomination:</h3>
+	<ul>
+	  {% for rem in nomination.remark_set.all %}
+	  {{ rem.as_li }}
+	  {% endfor %}
+	</ul>
+	{% endif %}
+	<hr class="hr6"/>
+	<br/>
+      </li>
+      {% endfor %}
+    </ul>
+    </div>
+  </div>
+  {% endif %}
+
+  <hr class="hr12"/>
+
+</section>
+
+<section id="Motions">
+  <div class="flex-container">
+    <div class="flex-greybox" id="MotionBox">
+      <h2>Submit a new Motion</h2>
+      <button id="submitMotionButton">Put a new Motion forward</button>
+      <form id="submitMotionForm" action="{% url 'virtualmeetings:put_motion_forward' VGM_id=VGM.id %}" method="post">
+	{% csrf_token %}
+	{% load crispy_forms_tags %}
+	{% crispy motion_form %}
+      </form>
+    </div>
+  </div>
+
+  <div class="row">
+    <div class="flex-container">
+      <div class="flex-greybox">
+	<h2>Motions under consideration</h2>
+      </div>
+    </div>
+  </div>
+  {% for key, val in motion_categories_dict.items %}
+  <div class="row">
+    <div class="col-1"></div>
+    <div class="flex-container">
+      <div class="flex-greybox">
+	<h3>{{ val }}:</h3>
+      </div>
+    </div>
+    <div class="col-1"></div>
+    <div class="col-10">
+    <ul>
+      {% for motion in VGM.motion_set.all %}
+      {% if motion.category == key %}
+      <li>
+	{{ motion.as_li }}
+	<br/>
+	<div class="opinionsDisplay">
+	  <h4>Your opinion on this Motion (voting deadline: {{ motion.voting_deadline|date:'y-m-d' }}):</h4>
+	  <form action="{% url 'virtualmeetings:vote_on_motion' motion_id=motion.id vote='A' %}" method="post">
+            {% csrf_token %}
+            <input type="submit" class="agree" value="Agree {{ motion.nr_A }} "/>
+	  </form>
+	  <form action="{% url 'virtualmeetings:vote_on_motion' motion_id=motion.id vote='N' %}" method="post">
+            {% csrf_token %}
+            <input type="submit" class="notsure" value="Not sure {{ motion.nr_N }}"/>
+	  </form>
+	  <form action="{% url 'virtualmeetings:vote_on_motion' motion_id=motion.id vote='D'%}" method="post">
+            {% csrf_token %}
+            <input type="submit" class="disagree" value="Disagree {{ motion.nr_D }}"/>
+	  </form>
+	  {% if request.user.contributor in motion.in_agreement.all %}
+	  <strong>(you have voted: Agreed)</strong>
+	  {% elif request.user.contributor in motion.in_notsure.all %}
+	  <strong>(you have voted: Not sure)</strong>
+	  {% elif request.user.contributor in motion.in_disagreement.all %}
+	  <strong>(you have voted: Disagree)</strong>
+	  {% endif %}
+	</div>
+	<br/><br/>
+	<button class="submitRemarkButton" id="remarkButton{{ motion.id }}">Add a remark on this Motion</button>
+	<div class="submitRemarkForm" id="remarkForm{{ motion.id }}">
+	  <form action="{% url 'virtualmeetings:add_remark_on_motion' motion_id=motion.id %}" method="post">
+	    {% csrf_token %}
+	    {{ remark_form.as_p }}
+	    <input type="submit" value="Submit" />
+	  </form>
+	</div>
+	{% if motion.remark_set.all %}
+	<h3>Remarks on this motion:</h3>
+	<ul>
+	  {% for rem in motion.remark_set.all %}
+	  {{ rem.as_li }}
+	  {% endfor %}
+	</ul>
+	{% endif %}
+	<hr class="hr6"/>
+	<br/>
+      </li>
+      {% endif %}
+      {% endfor %}
+    </ul>
+    </div>
+  </div>
+  {% endfor %}
+
+</section>
+
+
+{% endblock bodysup %}
diff --git a/virtualmeetings/templates/virtualmeetings/VGMs.html b/virtualmeetings/templates/virtualmeetings/VGMs.html
new file mode 100644
index 0000000000000000000000000000000000000000..a482a21dfadd11a7121e88458dd9bdb2df7ef9a0
--- /dev/null
+++ b/virtualmeetings/templates/virtualmeetings/VGMs.html
@@ -0,0 +1,26 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: VGMs{% endblock pagetitle %}
+
+{% load staticfiles %}
+
+{% block bodysup %}
+
+
+<section>
+  <div class="flex-container">
+    <div class="flex-greybox">
+      <h1>SciPost Virtual General Meetings</h1>
+    </div>
+  </div>
+
+  <ul>
+    {% for VGM in VGM_list %}
+    <li><a href="{% url 'virtualmeetings:VGM_detail' VGM_id=VGM.id %}">{{ VGM }}</a></li>
+    {% endfor %}
+  </ul>
+
+</section>
+
+
+{% endblock bodysup %}
diff --git a/virtualmeetings/tests.py b/virtualmeetings/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6
--- /dev/null
+++ b/virtualmeetings/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/virtualmeetings/urls.py b/virtualmeetings/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..556a7eb2b1e4dca34f7330c19ab6779aaac8a080
--- /dev/null
+++ b/virtualmeetings/urls.py
@@ -0,0 +1,24 @@
+
+from django.conf.urls import include, url
+from django.views.generic import TemplateView
+
+from . import views
+
+urlpatterns = [
+    url(r'^$', views.VGMs, name='VGMs'),
+    url(r'^VGM/(?P<VGM_id>[0-9]+)/$', views.VGM_detail, name='VGM_detail'),
+    url(r'^feedback/(?P<VGM_id>[0-9]+)$', views.feedback, name='feedback'),
+    url(r'^add_remark_on_feedback/(?P<VGM_id>[0-9]+)/(?P<feedback_id>[0-9]+)$',
+        views.add_remark_on_feedback, name='add_remark_on_feedback'),
+    url(r'^nominate_Fellow/(?P<VGM_id>[0-9]+)$', views.nominate_Fellow, name='nominate_Fellow'),
+    url(r'^add_remark_on_nomination/(?P<VGM_id>[0-9]+)/(?P<nomination_id>[0-9]+)$',
+        views.add_remark_on_nomination, name='add_remark_on_nomination'),
+    url(r'^vote_on_nomination/(?P<nomination_id>[0-9]+)/(?P<vote>[AND])$',
+        views.vote_on_nomination, name='vote_on_nomination'),
+    url(r'^put_motion_forward/(?P<VGM_id>[0-9]+)$',
+        views.put_motion_forward, name='put_motion_forward'),
+    url(r'^add_remark_on_motion/(?P<motion_id>[0-9]+)$',
+        views.add_remark_on_motion, name='add_remark_on_motion'),
+    url(r'^vote_on_motion/(?P<motion_id>[0-9]+)/(?P<vote>[AND])$',
+        views.vote_on_motion, name='vote_on_motion'),
+]
diff --git a/virtualmeetings/views.py b/virtualmeetings/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee357ca3f74aac592483d38e4500a75d1c855bba
--- /dev/null
+++ b/virtualmeetings/views.py
@@ -0,0 +1,252 @@
+import datetime
+
+from django.contrib.auth.decorators import login_required, permission_required
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404, render
+from django.template import Context, Template
+from django.utils import timezone
+
+from .constants import motion_categories_dict
+from .forms import FeedbackForm, NominationForm, MotionForm
+from .models import VGM, Feedback, Nomination, Motion
+
+from scipost.forms import RegistrationInvitation, RemarkForm
+from scipost.models import Contributor, Remark
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs')
+def VGMs(request):
+    VGM_list = VGM.objects.all().order_by('start_date')
+    context = {'VGM_list': VGM_list}
+    return render(request, 'virtualmeetings/VGMs.html', context)
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs')
+def VGM_detail(request, VGM_id):
+    VGM_instance = get_object_or_404(VGM, id=VGM_id)
+    VGM_information = Template(VGM_instance.information).render(Context({}))
+    feedback_received = Feedback.objects.filter(VGM=VGM_instance).order_by('date')
+    feedback_form = FeedbackForm()
+    current_Fellows = Contributor.objects.filter(
+        user__groups__name='Editorial College').order_by('user__last_name')
+    sent_inv_Fellows = RegistrationInvitation.objects.filter(
+        invitation_type='F', responded=False)
+    pending_inv_Fellows = sent_inv_Fellows.filter(declined=False).order_by('last_name')
+    declined_inv_Fellows = sent_inv_Fellows.filter(declined=True).order_by('last_name')
+    nomination_form = NominationForm()
+    nominations = Nomination.objects.filter(accepted=None).order_by('last_name')
+    motion_form = MotionForm()
+    remark_form = RemarkForm()
+    context = {
+        'VGM': VGM_instance,
+        'VGM_information': VGM_information,
+        'feedback_received': feedback_received,
+        'feedback_form': feedback_form,
+        'current_Fellows': current_Fellows,
+        'pending_inv_Fellows': pending_inv_Fellows,
+        'declined_inv_Fellows': declined_inv_Fellows,
+        'nominations': nominations,
+        'nomination_form': nomination_form,
+        'motion_categories_dict': motion_categories_dict,
+        'motion_form': motion_form,
+        'remark_form': remark_form,
+    }
+    return render(request, 'virtualmeetings/VGM_detail.html', context)
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs')
+def feedback(request, VGM_id=None):
+    if request.method == 'POST':
+        feedback_form = FeedbackForm(request.POST)
+        if feedback_form.is_valid():
+            feedback = Feedback(by=request.user.contributor,
+                                date=timezone.now().date(),
+                                feedback=feedback_form.cleaned_data['feedback'],)
+            if VGM_id:
+                VGM_instance = get_object_or_404(VGM, id=VGM_id)
+                feedback.VGM = VGM_instance
+            feedback.save()
+            ack_message = 'Your feedback has been received.'
+            context = {'ack_message': ack_message}
+            if VGM_id:
+                context['followup_message'] = 'Return to the '
+                context['followup_link'] = reverse('virtualmeetings:VGM_detail',
+                                                   kwargs={'VGM_id': VGM_id})
+                context['followup_link_label'] = 'VGM page'
+            return render(request, 'scipost/acknowledgement.html', context)
+        else:
+            errormessage = 'The form was not filled properly.'
+            return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    else:
+        errormessage = 'This view can only be posted to.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs', raise_exception=True)
+def add_remark_on_feedback(request, VGM_id, feedback_id):
+    # contributor = request.user.contributor
+    feedback = get_object_or_404(Feedback, pk=feedback_id)
+    if request.method == 'POST':
+        remark_form = RemarkForm(request.POST)
+        if remark_form.is_valid():
+            remark = Remark(contributor=request.user.contributor,
+                            feedback=feedback,
+                            date=timezone.now(),
+                            remark=remark_form.cleaned_data['remark'])
+            remark.save()
+            return HttpResponseRedirect('/VGM/' + str(VGM_id) +
+                                        '/#feedback_id' + str(feedback.id))
+        else:
+            errormessage = 'The form was invalidly filled.'
+            return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    else:
+        errormessage = 'This view can only be posted to.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs')
+def nominate_Fellow(request, VGM_id):
+    VGM_instance = get_object_or_404(VGM, id=VGM_id)
+    if request.method == 'POST':
+        nomination_form = NominationForm(request.POST)
+        if nomination_form.is_valid():
+            nomination = Nomination(
+                VGM=VGM_instance,
+                by=request.user.contributor,
+                date=timezone.now().date(),
+                first_name=nomination_form.cleaned_data['first_name'],
+                last_name=nomination_form.cleaned_data['last_name'],
+                discipline=nomination_form.cleaned_data['discipline'],
+                expertises=nomination_form.cleaned_data['expertises'],
+                webpage=nomination_form.cleaned_data['webpage'],
+                voting_deadline=VGM_instance.end_date + datetime.timedelta(days=7),
+            )
+            nomination.save()
+            nomination.update_votes(request.user.contributor.id, 'A')
+            ack_message = 'The nomination has been registered.'
+            context = {'ack_message': ack_message,
+                       'followup_message': 'Return to the ',
+                       'followup_link': reverse('virtualmeetings:VGM_detail',
+                                                kwargs={'VGM_id': VGM_id}),
+                       'followup_link_label': 'VGM page'}
+            return render(request, 'scipost/acknowledgement.html', context)
+        else:
+            errormessage = 'The form was not filled properly.'
+            return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    else:
+        errormessage = 'This view can only be posted to.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs', raise_exception=True)
+def add_remark_on_nomination(request, VGM_id, nomination_id):
+    # contributor = request.user.contributor
+    nomination = get_object_or_404(Nomination, pk=nomination_id)
+    if request.method == 'POST':
+        remark_form = RemarkForm(request.POST)
+        if remark_form.is_valid():
+            remark = Remark(contributor=request.user.contributor,
+                            nomination=nomination,
+                            date=timezone.now(),
+                            remark=remark_form.cleaned_data['remark'])
+            remark.save()
+            return HttpResponseRedirect('/VGM/' + str(VGM_id) +
+                                        '/#nomination_id' + str(nomination.id))
+        else:
+            errormessage = 'The form was invalidly filled.'
+            return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    else:
+        errormessage = 'This view can only be posted to.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs', raise_exception=True)
+def vote_on_nomination(request, nomination_id, vote):
+    contributor = request.user.contributor
+    nomination = get_object_or_404(Nomination, pk=nomination_id)
+    if timezone.now() > nomination.voting_deadline:
+        errormessage = 'The voting deadline on this nomination has passed.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    nomination.update_votes(contributor.id, vote)
+    return HttpResponseRedirect('/VGM/' + str(nomination.VGM.id) +
+                                '/#nomination_id' + str(nomination.id))
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs')
+def put_motion_forward(request, VGM_id):
+    VGM_instance = get_object_or_404(VGM, id=VGM_id)
+    if timezone.now().date() > VGM_instance.end_date:
+        errormessage = 'This VGM has ended. No new motions can be put forward.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    if request.method == 'POST':
+        motion_form = MotionForm(request.POST)
+        if motion_form.is_valid():
+            motion = Motion(
+                category=motion_form.cleaned_data['category'],
+                VGM=VGM_instance,
+                background=motion_form.cleaned_data['background'],
+                motion=motion_form.cleaned_data['motion'],
+                put_forward_by=request.user.contributor,
+                date=timezone.now().date(),
+                voting_deadline=VGM_instance.end_date + datetime.timedelta(days=7),
+            )
+            motion.save()
+            motion.update_votes(request.user.contributor.id, 'A')
+            ack_message = 'Your motion has been registered.'
+            context = {'ack_message': ack_message,
+                       'followup_message': 'Return to the ',
+                       'followup_link': reverse('virtualmeetings:VGM_detail',
+                                                kwargs={'VGM_id': VGM_id}),
+                       'followup_link_label': 'VGM page'}
+            return render(request, 'scipost/acknowledgement.html', context)
+        else:
+            errormessage = 'The form was not filled properly.'
+            return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    else:
+        errormessage = 'This view can only be posted to.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs', raise_exception=True)
+def add_remark_on_motion(request, motion_id):
+    # contributor = request.user.contributor
+    motion = get_object_or_404(Motion, pk=motion_id)
+    if request.method == 'POST':
+        remark_form = RemarkForm(request.POST)
+        if remark_form.is_valid():
+            remark = Remark(contributor=request.user.contributor,
+                            motion=motion,
+                            date=timezone.now(),
+                            remark=remark_form.cleaned_data['remark'])
+            remark.save()
+            return HttpResponseRedirect('/VGM/' + str(motion.VGM.id) +
+                                        '/#motion_id' + str(motion.id))
+        else:
+            errormessage = 'The form was invalidly filled.'
+            return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    else:
+        errormessage = 'This view can only be posted to.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+
+
+@login_required
+@permission_required('scipost.can_attend_VGMs', raise_exception=True)
+def vote_on_motion(request, motion_id, vote):
+    contributor = request.user.contributor
+    motion = get_object_or_404(Motion, pk=motion_id)
+    if timezone.now() > motion.voting_deadline:
+        errormessage = 'The voting deadline on this motion has passed.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    motion.update_votes(contributor.id, vote)
+    return HttpResponseRedirect('/VGM/' + str(motion.VGM.id) +
+                                '/#motion_id' + str(motion.id))