diff --git a/SciPost_v1/settings.py b/SciPost_v1/settings.py
index 2cd8bb1716d85215dcd2b19e44996cea52aef773..663a264900986b2295e2280dc8e822bc0a184f56 100644
--- a/SciPost_v1/settings.py
+++ b/SciPost_v1/settings.py
@@ -60,7 +60,6 @@ INSTALLED_APPS = (
     'django_mathjax',
     'captcha',
     'crispy_forms',
-    'mptt',
     'commentaries',
     'comments',
     'journals',
diff --git a/scipost/admin.py b/scipost/admin.py
index 0c57c906b26e55f5399acf9bc5358f8e0acf2389..ccfcd132ba3708b9bdf4a9c488c37081e1868784 100644
--- a/scipost/admin.py
+++ b/scipost/admin.py
@@ -26,3 +26,9 @@ admin.site.register(AuthorshipClaim)
 #admin.site.register(Opinion)
 
 admin.site.register(Permission)
+
+admin.site.register(Team)
+
+admin.site.register(Graph)
+
+admin.site.register(Node)
diff --git a/scipost/forms.py b/scipost/forms.py
index 9b254c2417115e43b60fb7708e25a5fe1df41692..2f22a11e05e4c50f400ce27b2ec85b52c7a21621 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -1,5 +1,7 @@
 from django import forms 
 
+from django.db.models import Q
+
 from django_countries import countries
 from django_countries.widgets import CountrySelectWidget
 from django_countries.fields import LazyTypedChoiceField
@@ -102,3 +104,36 @@ class AuthorshipClaimForm(forms.Form):
 #    class Meta:
 #        model = Assessment
 #        fields = ['relevance', 'importance', 'clarity', 'validity', 'rigour', 'originality', 'significance']
+
+
+class CreateTeamForm(forms.ModelForm):
+    class Meta:
+        model = Team
+        fields = ['name', ]
+
+    def __init__(self, *args, **kwargs):
+        super(CreateTeamForm, self).__init__(*args, **kwargs)
+        self.fields['name'].widget.attrs.update({'size': 30, 'placeholder': 'Descriptive name for the new Team'})
+
+
+class AddTeamMemberForm(forms.Form):
+    def __init__(self, *args, **kwargs):
+        super(AddTeamMemberForm, self).__init__(*args, **kwargs)
+        self.fields['last_name'].widget.attrs.update({'size': 20, 'placeholder': 'Search in contributors database'})
+
+    last_name = forms.CharField()
+
+
+class CreateGraphForm(forms.ModelForm):
+    class Meta:
+        model = Graph
+        fields = ['title', 'description', 'private', 'teams_with_access']
+
+    def __init__(self, *args, **kwargs):
+        contributor = kwargs.pop('contributor')
+        super(CreateGraphForm, self).__init__(*args, **kwargs)
+        self.fields['title'].widget.attrs.update({'size': 30, 'placeholder': 'Descriptive title for the new Graph'})
+        self.fields['private'].widget.attrs.update({'placeholder': 'Private?'})
+        self.fields['teams_with_access'] = forms.ModelMultipleChoiceField(
+            queryset=Team.objects.filter(Q(leader=contributor) | Q(members__in=[contributor])))
+        self.fields['teams_with_access'].widget.attrs.update({'placeholder': 'Team to be given access rights:'})
diff --git a/scipost/management/commands/add_groups_and_permissions.py b/scipost/management/commands/add_groups_and_permissions.py
index 656f947696d0f8571c0d4ab2ff607df06a5b89a1..8d55899986417d60b1e83d4744762dfc55e1ff26 100644
--- a/scipost/management/commands/add_groups_and_permissions.py
+++ b/scipost/management/commands/add_groups_and_permissions.py
@@ -87,6 +87,20 @@ class Command(BaseCommand):
             name= 'Can act as a referee and submit reports on Submissions',
             content_type=content_type)
 
+        # Teams
+        can_create_team, created = Permission.objects.get_or_create(
+            codename='can_create_team',
+            name= 'Can create a new Team',
+            content_type=content_type)
+
+        # Graphs
+        can_create_graph, created = Permission.objects.get_or_create(
+            codename='can_create_graph',
+            name= 'Can create a new Graph',
+            content_type=content_type)
+
+
+
         # Assign permissions to groups
         SciPostAdmin.permissions.add(can_manage_registration_invitations,
                                      can_vet_registration_requests,
diff --git a/scipost/models.py b/scipost/models.py
index eda478fd7b2788d0425ffd7575b0c770bc8ddb3f..18b7cec94a27eee579a1b2f9574ea7d73b93c733 100644
--- a/scipost/models.py
+++ b/scipost/models.py
@@ -6,8 +6,6 @@ from django.template import Template, Context
 
 from django_countries.fields import CountryField
 
-from mptt.models import MPTTModel, TreeForeignKey
-
 from .models import *
 
 SCIPOST_DISCIPLINES = (
@@ -267,28 +265,79 @@ class Team(models.Model):
     leader = models.ForeignKey(Contributor)
     members = models.ManyToManyField (Contributor, blank=True, related_name='team_members')
     name = models.CharField(max_length=20)
+    established = models.DateField(default=timezone.now)
 
     def __str__(self):
-        return name + ' (led by ' + leader.user.first_name + ' ' + leader.user.last_name + ')'
-    
+        return self.name + ' (led by ' + self.leader.user.first_name + ' ' + self.leader.user.last_name + ')'
+
+    def header_as_li(self):
+        context = Context({'name': self.name,})
+        output = '<li><p>Team {{ name }}, led by ' + self.leader.user.first_name + ' ' + self.leader.user.last_name + '</p>'
+        output += '<p>Members: '
+        if not self.members.all():
+            output += '(none yet, except for the leader)'
+        else :
+            for member in self.members.all():
+                output += member.user.first_name + ' ' + member.user.last_name + ', '
+        output += '</p></li>'
+        template = Template(output)
+        return template.render(context)
 
-#########
-# Lists #
-#########
 
-class Node(MPTTModel):
+##########
+# Graphs #
+##########
+
+class Graph(models.Model):
+    """ 
+    A Graph is a collection of Nodes with directed arrows,
+    representing e.g. a reading list, exploration path, etc.
+    If private, only the teams in teams_with_access can see/edit it.
     """
-    Node of a list (tree of submissions, commentaries, thesislinks). 
-    Requires django-mptt.
+    owner = models.ForeignKey(Contributor)
+    private = models.BooleanField(default=True)
+    teams_with_access = models.ManyToManyField(Team, blank=True)
+    title = models.CharField(max_length=100)
+    description = models.TextField(blank=True, null=True)
+    created = models.DateTimeField(default=timezone.now)
+
+    def __str__(self):
+        return self.title[:30] + ' (owner: ' + self.owner.user.first_name + ' ' + self.owner.user.last_name + ')'
+
+    def header_as_li(self):
+        context = Context({'id': self.id, 'title': self.title, 
+                           'first_name': self.owner.user.first_name, 
+                           'last_name': self.owner.user.last_name})
+        template = Template('''
+           <li><p>Graph <a href="{% url 'scipost:graph' graph_id=id %}">{{ title }}</a> (owner: {{ first_name }} {{ last_name }})</li>
+        ''')
+        return template.render(context)
+
+    def contents(self):
+        context = Context({})
+        output = self.description
+        template = Template(output)
+        return template.render(context)
+
+
+class Node(models.Model):
+    """
+    Node of a graph (directed).
+    Each node is composed of a set of submissions, commentaries, thesislinks. 
+    Accessibility rights are set in the Graph ForeignKey.
     """
-    owner = models.ForeignKey(Team)
+    graph = models.ForeignKey(Graph, default=None)
+    added_by = models.ForeignKey(Contributor, default=None)
+    created = models.DateTimeField(default=timezone.now)
     name = models.CharField(max_length=100)
-    private = models.BooleanField(default=True)
-    parent = TreeForeignKey('self', blank=True, null=True, related_name='children', db_index=True)
+    arcs_in = models.ManyToManyField('self', blank=True, related_name='node_arcs_in') # arcs pointing into this node
     description = models.TextField(blank=True, null=True)
     submissions = models.ManyToManyField('submissions.Submission', blank=True, related_name='node_submissions')
     commentaries = models.ManyToManyField('commentaries.Commentary', blank=True, related_name='node_commentaries')
     thesislinks = models.ManyToManyField('theses.ThesisLink', blank=True, related_name='node_thesislinks')
     annotation = models.TextField(blank=True, null=True)
 
+    def __str__(self):
+        return self.graph.title[:20] + ': ' + self.name[:20] + ' (owner: ' + self.owner.user.first_name + ' ' + self.owner.user.last_name + ')'
+
     
diff --git a/scipost/static/scipost/SciPost.css b/scipost/static/scipost/SciPost.css
index 650ee53420c276fa74a559e39b68065fb5d9b52c..7666295725e604cba882a58504bc80c4486c0187 100644
--- a/scipost/static/scipost/SciPost.css
+++ b/scipost/static/scipost/SciPost.css
@@ -52,6 +52,7 @@ hr.hr12 {
 ul.personalTabMenu {
   background-color: #dddddd;
   display: inline-block;
+  font-size: 12px;
   padding: 0px;
 }
 ul.personalTabMenu li {
diff --git a/scipost/templates/scipost/add_team_member.html b/scipost/templates/scipost/add_team_member.html
new file mode 100644
index 0000000000000000000000000000000000000000..de02025bc93ae13ea7efe374da945eda207eb5e7
--- /dev/null
+++ b/scipost/templates/scipost/add_team_member.html
@@ -0,0 +1,35 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: add team member{% endblock pagetitle %}
+
+{% block bodysup %}
+
+<section>
+  <h1>Add Members to Team</h1>
+  <ul>
+    {{ team.header_as_li }}
+  </ul>
+
+  {% if contributors_found %}
+  <h3>Identified as contributor:</h3>
+  <table>
+    {% for contributor in contributors_found %}
+    <tr><td>{{ contributor.user.first_name }} {{ contributor.user.last_name }}</td><td>&nbsp;</td>
+      <td><a href="{% url 'scipost:add_team_member' team_id=team.id contributor_id=contributor.id %}">Add to the Team</a></td></tr>
+    {% endfor %}
+  </table>
+  {% elif add_team_member_form.has_changed %}
+  <p>No Contributor with this last name could be identified.</p>
+  {% else %}
+  <p>Add a member to the Team:</p>
+  <form action="{% url 'scipost:add_team_member' team_id=team.id %}" method="post">
+    {% csrf_token %}
+    {{ add_team_member_form }}
+    <input type="submit" value="Search" />
+  </form>
+  {% endif %}
+
+
+</section>
+
+{% endblock bodysup %}
diff --git a/scipost/templates/scipost/create_graph.html b/scipost/templates/scipost/create_graph.html
new file mode 100644
index 0000000000000000000000000000000000000000..0a731d8072c5776b8d584f9338212447b86ad0ed
--- /dev/null
+++ b/scipost/templates/scipost/create_graph.html
@@ -0,0 +1,22 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: create graph{% endblock pagetitle %}
+
+{% block bodysup %}
+
+<section>
+  {% if graphcreated %}
+  <p>{{ message }}</p>
+  {% else %}
+  <h1>Create a new Graph</h1>
+  <form action="{% url 'scipost:create_graph' %}" method="post">
+    {% csrf_token %}
+    <table>
+      {{ create_graph_form.as_table }}
+    </table>
+    <input type="submit" value="Create Graph" />
+  </form>
+  {% endif %}
+</section>
+
+{% endblock bodysup %}
diff --git a/scipost/templates/scipost/create_team.html b/scipost/templates/scipost/create_team.html
new file mode 100644
index 0000000000000000000000000000000000000000..e4e83002e69d1c37e07050aade4575b63be53a1d
--- /dev/null
+++ b/scipost/templates/scipost/create_team.html
@@ -0,0 +1,30 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: create team{% endblock pagetitle %}
+
+{% block bodysup %}
+
+<section>
+  {% if teamcreated %}
+  <p>{{ message }}</p>
+
+  <form action="{% url 'scipost:add_team_member' team_id=team.id %}" method="post">
+    {% csrf_token %}
+    <table>
+      {{ add_team_member_form.as_table }} 
+    </table>
+    <input type="submit" value="Search" />
+  </form>
+  {% else %}
+  <h1>Create a new collaborative Team</h1>
+  <form action="{% url 'scipost:create_team' %}" method="post">
+    {% csrf_token %}
+    <table>
+      {{ create_team_form.as_table }}
+    </table>
+    <input type="submit" value="Create Team" />
+  </form>
+  {% endif %}
+</section>
+
+{% endblock bodysup %}
diff --git a/scipost/templates/scipost/graph.html b/scipost/templates/scipost/graph.html
new file mode 100644
index 0000000000000000000000000000000000000000..8b14e6e4cacdae6374fe9c9493d7a8985f2cc8f1
--- /dev/null
+++ b/scipost/templates/scipost/graph.html
@@ -0,0 +1,15 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: graph{% endblock pagetitle %}
+
+{% block bodysup %}
+
+<section>
+  <h1>Graph</h1>
+
+  {{ graph.contents }}
+
+
+</section>
+
+{% endblock bodysup %}
diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html
index a5a3ecf9c6b42fc34a90a34e7e01adf93f7a50c6..4225cd0847feb51a3ca0716127c790d829242094 100644
--- a/scipost/templates/scipost/personal_page.html
+++ b/scipost/templates/scipost/personal_page.html
@@ -60,6 +60,18 @@
     $(".TabSection").hide();
     $("#AuthorReplies").show();
    });
+   $("#TeamsTab").click(function(){
+    $(".TabItem").attr("class", "TabItem inactive");
+    $("#TeamsTab").attr("class", "TabItem active");
+    $(".TabSection").hide();
+    $("#Teams").show();
+   });
+   $("#GraphsTab").click(function(){
+    $(".TabItem").attr("class", "TabItem inactive");
+    $("#GraphsTab").attr("class", "TabItem active");
+    $(".TabSection").hide();
+    $("#Graphs").show();
+   });
   });
 </script>
 
@@ -83,13 +95,21 @@
 
   <ul class="personalTabMenu">
     <li><a class="TabItem" id="AccountTab">Account</a></li>
+    {% if request.user|is_in_group:'SciPost Administrators' or request.user|is_in_group:'Editorial College' or request.user|is_in_group:'Vetting Editors' %}
     <li><a class="TabItem" id="EdActionTab">Editorial Actions</a></li>
+    {% endif %}
     <li><a class="TabItem" id="RefereeingTab">Refereeing</a></li>
     <li><a class="TabItem" id="SubmissionsTab">Submissions</a></li>
     <li><a class="TabItem" id="CommentariesTab">Commentaries</a></li>
     <li><a class="TabItem" id="ThesesTab">Theses</a></li>
     <li><a class="TabItem" id="CommentsTab">Comments</a></li>
     <li><a class="TabItem" id="AuthorRepliesTab">Author Replies</a></li>
+    {% if perms.scipost.can_create_team %}
+    <li><a class="TabItem" id="TeamsTab">Teams</a></li>
+    {% endif %}
+    {% if perms.scipost.can_create_graph %}
+    <li><a class="TabItem" id="GraphsTab">Graphs</a></li>
+    {% endif %}
   </ul>
 </section>
 
@@ -326,6 +346,62 @@
 </section>
 {% endif %}
 
+<section class="TabSection" id="Teams">
+  <hr class="hr12">
+  <div class="flex-greybox">
+    <h1>Teams</h1>
+    <ul>
+      <li><a href="{% url 'scipost:create_team' %}">Create a new Team</a></li>
+    </ul>
+  </div>
+  {% if teams_led %}
+  <h3>Teams you are leading:</h3>
+  <ul>
+    {% for team in teams_led %}
+    {{ team.header_as_li }}
+    <a href="{% url 'scipost:add_team_member' team_id=team.id %}">Add a member</a>
+    {% endfor %}
+  </ul>
+  {% endif %}
+  {% if teams %}
+  <h3>Teams of which you are a member:</h3>
+  <ul>
+    {% for team in teams %}
+    {{ team.header_as_li }}
+    {% endfor %}
+  </ul>
+  {% endif %}
+
+</section>
+
+<section class="TabSection" id="Graphs">
+  <hr class="hr12">
+  <div class="flex-greybox">
+    <h1>Graphs</h1>
+    <ul>
+      <li><a href="{% url 'scipost:create_graph' %}">Create a new Graph</a></li>
+    </ul>
+  </div>
+
+  {% if graphs_owned %}
+  <h3>Graphs you own:</h3>
+  <ul>
+    {% for graph in graphs_owned %}
+    {{ graph.header_as_li }}
+    {% endfor %}
+  </ul>
+  {% endif %}
+  {% if graphs_private %}
+  <h3>Private graphs which you can access:</h3>
+  <ul>
+    {% for graph in graphs_private %}
+    {{ graph.header_as_li }}
+    {% endfor %}
+  </ul>
+  {% endif %}
+
+</section>
+
 {% endif %}
 
 {% endblock bodysup %}
diff --git a/scipost/urls.py b/scipost/urls.py
index adcc02176a004b8b1729fb3131156adf6f5687f0..299141a0a7688e4f3dc4c91e17e278b5eed68bdd 100644
--- a/scipost/urls.py
+++ b/scipost/urls.py
@@ -60,4 +60,14 @@ urlpatterns = [
 
     # Feeds
     url(r'^latest_comment/feed/$', LatestCommentFeed()),
+
+    # Teams
+    url(r'^create_team$', views.create_team, name='create_team'),
+    url(r'^add_team_member/(?P<team_id>[0-9]+)$', views.add_team_member, name='add_team_member'),
+    url(r'^add_team_member/(?P<team_id>[0-9]+)/(?P<contributor_id>[0-9]+)$', views.add_team_member, name='add_team_member'),
+
+    # Graphs
+    url(r'^create_graph$', views.create_graph, name='create_graph'),
+#    url(r'^add_graph_node/(?P<graph_id>[0-9]+)$', views.add_graph_node, name='add_graph_node'),
+    url(r'^graph/(?P<graph_id>[0-9]+)$', views.graph, name='graph'),
 ]
diff --git a/scipost/views.py b/scipost/views.py
index 3c95c514ec936e023bba5762d50c061dcf9de229..f346e7ddd312064fbb15103fcebca59124917524 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -360,6 +360,10 @@ def personal_page(request):
         nr_thesis_authorships_to_claim = ThesisLink.objects.filter(author__contains=contributor.user.last_name).exclude(author_as_cont__in=[contributor]).exclude(author_claims__in=[contributor]).exclude(author_false_claims__in=[contributor]).count()
         own_comments = Comment.objects.filter(author=contributor,is_author_reply=False).order_by('-date_submitted')
         own_authorreplies = Comment.objects.filter(author=contributor,is_author_reply=True).order_by('-date_submitted')
+        teams_led = Team.objects.filter(leader=contributor)
+        teams = Team.objects.filter(members__in=[contributor])
+        graphs_owned = Graph.objects.filter(owner=contributor)
+        graphs_private = Graph.objects.filter(Q(teams_with_access__leader=contributor) | Q(teams_with_access__members__in=[contributor]))
         context = {'contributor': contributor, 'nr_reg_to_vet': nr_reg_to_vet, 
                    'nr_reg_awaiting_validation': nr_reg_awaiting_validation, 
                    'nr_commentary_page_requests_to_vet': nr_commentary_page_requests_to_vet, 
@@ -378,7 +382,12 @@ def personal_page(request):
                    'own_submissions': own_submissions, 
                    'own_commentaries': own_commentaries,
                    'own_thesislinks': own_thesislinks,
-                   'own_comments': own_comments, 'own_authorreplies': own_authorreplies}
+                   'own_comments': own_comments, 'own_authorreplies': own_authorreplies,
+                   'teams_led': teams_led,
+                   'teams': teams,
+                   'graphs_owned': graphs_owned,
+                   'graphs_private': graphs_private,
+                   }
         return render(request, 'scipost/personal_page.html', context)
     else:
         form = AuthenticationForm()
@@ -572,3 +581,87 @@ def contributor_info(request, contributor_id):
         context = {'form': form}
         return render(request, 'scipost/login.html', context)
 
+
+
+#########
+# Teams #
+#########
+
+@permission_required('scipost.can_create_team', raise_exception=True)
+def create_team(request):
+    if request.method == "POST":
+        create_team_form = CreateTeamForm(request.POST)
+        if create_team_form.is_valid():
+            newteam = Team(leader=request.user.contributor,
+                           name=create_team_form.cleaned_data['name'],
+                           established=timezone.now())
+            newteam.save()
+            return redirect(reverse('scipost:add_team_member', kwargs={'team_id': newteam.id}))
+    else:
+        create_team_form = CreateTeamForm()
+    add_team_member_form = AddTeamMemberForm()
+    context = {'create_team_form': create_team_form, 
+               'add_team_member_form': add_team_member_form}
+    return render(request, 'scipost/create_team.html', context)
+
+@permission_required('scipost.can_create_team', raise_exception=True)
+def add_team_member(request, team_id, contributor_id=None):
+    team = get_object_or_404(Team, pk=team_id)
+    contributors_found = None
+    if contributor_id is not None:
+        contributor = get_object_or_404(Contributor, pk=contributor_id)
+        team.members.add(contributor)
+        team.save()
+        return redirect(reverse('scipost:add_team_member', kwargs={'team_id': team_id}))
+    if request.method == "POST":
+        add_team_member_form = AddTeamMemberForm(request.POST)
+        if add_team_member_form.is_valid():
+            contributors_found = Contributor.objects.filter(user__last_name__icontains=add_team_member_form.cleaned_data['last_name'])
+    else:
+        add_team_member_form = AddTeamMemberForm()
+    context = {'team': team, 'add_team_member_form': add_team_member_form,
+               'contributors_found': contributors_found}
+    return render(request, 'scipost/add_team_member.html', context)
+
+
+##########
+# Graphs #
+##########
+
+@permission_required('scipost.can_create_graph', raise_exception=True)
+def create_graph(request):
+    graphcreated = False
+    message = None
+    if request.method == "POST":
+        create_graph_form = CreateGraphForm(request.POST, contributor=request.user.contributor)
+        if create_graph_form.is_valid():
+            newgraph = Graph(owner=request.user.contributor,
+                             title=create_graph_form.cleaned_data['title'],
+                             private=create_graph_form.cleaned_data['private'],
+                             teams_with_access=form.cleaned_data['teams_with_access'],
+                             created=timezone.now())
+            newgraph.save()
+            graphcreated = True
+            message = 'Graph' + create_graph_form.cleaned_data['title'] + ' was successfully created.'
+    else:
+        create_graph_form = CreateGraphForm(contributor=request.user.contributor)
+    context = {'create_graph_form': create_graph_form, 'graphcreated': graphcreated,
+               'message': message}
+    return render(request, 'scipost/create_graph.html', context)
+
+
+@permission_required('scipost.can_create_graph', raise_exception=True)
+def graph(request, graph_id, node_id=None):
+    graph = get_object_or_404(Graph, pk=graph_id)
+#    if node_id is not None:
+#        node = get_object_or_404(Node, pk=node_id)
+#    base_node = Node.objects.filter(graph=graph, arcs_in=None)
+    context = {'graph': graph}
+    return render(request, 'scipost/graph.html', context)
+
+@permission_required('scipost.can_create_graph', raise_exception=True)
+def add_node_upstream(request, graph_id, node_id):
+    """ Adds a node upstream from the one at node_id """
+    node = get_object_or_404(Node, pk=node_id)
+    
+
diff --git a/submissions/forms.py b/submissions/forms.py
index 39ccc8753078b249e04422729eb907a90146a2b0..14136ed30d904ff5499cebea3ca5f55b791b88d1 100644
--- a/submissions/forms.py
+++ b/submissions/forms.py
@@ -40,8 +40,6 @@ class AssignSubmissionForm(forms.Form):
 #                                                user__contributor__specializations__contains=[specialization,] # Reactivate later on, once the Editorial College is large enough
                                                 ), required=True, label='Select an Editor-in-charge')
 
-    #editor_in_charge = forms.ModelChoiceField(queryset=...)
-
 
 class ConsiderAssignmentForm(forms.Form):
     accept = forms.ChoiceField(widget=forms.RadioSelect, choices=ASSIGNMENT_BOOL, label="Are you willing to take charge of this Submission?")