diff --git a/profiles/admin.py b/profiles/admin.py
index 1b54b2c2a72c5d9253e0c89c62ec59d0c5cce768..d0e860a7fbdcafc4a33b4e80d029d04ef8a3dd3a 100644
--- a/profiles/admin.py
+++ b/profiles/admin.py
@@ -4,7 +4,7 @@ __license__ = "AGPL v3"
 
 from django.contrib import admin
 
-from .models import Profile, ProfileEmail, ProfileNonDuplicates
+from .models import Profile, ProfileEmail, ProfileNonDuplicates, Affiliation
 
 
 class ProfileEmailInline(admin.TabularInline):
@@ -12,10 +12,15 @@ class ProfileEmailInline(admin.TabularInline):
     extra = 0
 
 
+class AffiliationInline(admin.TabularInline):
+    model = Affiliation
+    extra = 0
+
+
 class ProfileAdmin(admin.ModelAdmin):
     list_display = ['__str__', 'email', 'discipline', 'expertises', 'has_active_contributor']
     search_fields = ['first_name', 'last_name', 'emails__email', 'orcid_id']
-    inlines = [ProfileEmailInline]
+    inlines = [ProfileEmailInline, AffiliationInline]
 
 admin.site.register(Profile, ProfileAdmin)
 
diff --git a/profiles/constants.py b/profiles/constants.py
index b9ac6f92693bab316ddad1cc133615f6deb7a857..29bdde8df464a1d2c73f693e5b4c975dd44de17a 100644
--- a/profiles/constants.py
+++ b/profiles/constants.py
@@ -36,5 +36,5 @@ AFFILIATION_CATEGORIES = (
     (AFFILIATION_CATEGORY_EMPLOYED_PhD, 'PhD candidate'),
     (AFFILIATION_CATEGORY_ASSOCIATE_SCIENTIST, 'Associate Scientist'),
     (AFFILIATION_CATEGORY_CONSULTANT, 'Consultant'),
-    (AFFILIATION_CATEGORY_VISITOR, 'Visotor'),
+    (AFFILIATION_CATEGORY_VISITOR, 'Visitor'),
 )
diff --git a/profiles/forms.py b/profiles/forms.py
index dfba810721e71273c78feab0bfdf397948b161e2..adc7c9ad5315b26ff86583712f300245c29577e1 100644
--- a/profiles/forms.py
+++ b/profiles/forms.py
@@ -5,6 +5,8 @@ __license__ = "AGPL v3"
 from django import forms
 from django.shortcuts import get_object_or_404
 
+from ajax_select.fields import AutoCompleteSelectField
+
 from common.forms import ModelChoiceFieldwithid
 from invitations.models import RegistrationInvitation
 from journals.models import UnregisteredAuthor
@@ -12,7 +14,7 @@ from ontology.models import Topic
 from scipost.models import Contributor
 from submissions.models import RefereeInvitation
 
-from .models import Profile, ProfileEmail
+from .models import Profile, ProfileEmail, Affiliation
 
 
 class ProfileForm(forms.ModelForm):
@@ -165,3 +167,16 @@ class ProfileEmailForm(forms.ModelForm):
         """Save to a profile."""
         self.instance.profile = self.profile
         return super().save()
+
+
+class AffiliationForm(forms.ModelForm):
+    organization = AutoCompleteSelectField('organization_lookup')
+
+    class Meta:
+        model = Affiliation
+        fields = ['profile', 'organization', 'category',
+                  'description', 'date_from', 'date_until']
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields['profile'].widget = forms.HiddenInput()
diff --git a/profiles/migrations/0019_auto_20190327_1520.py b/profiles/migrations/0019_auto_20190327_1520.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee2a8810c796494d4e7f5922013c2019a266a264
--- /dev/null
+++ b/profiles/migrations/0019_auto_20190327_1520.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2019-03-27 14:20
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('profiles', '0018_affiliation'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='affiliation',
+            options={'ordering': ['profile__last_name', 'profile__first_name', 'date_until']},
+        ),
+        migrations.AlterField(
+            model_name='affiliation',
+            name='category',
+            field=models.CharField(choices=[('employed_prof_full', 'Full Professor'), ('employed_prof_associate', 'Associate Professor'), ('employed_prof_assistant', 'Assistant Professor'), ('employed_prof_emeritus', 'Emeritus Professor'), ('employed_permanent_staff', 'Permanent Staff'), ('employed_fixed_term_staff', 'Fixed Term Staff'), ('employed_tenure_track', 'Tenure Tracker'), ('employed_postdoc', 'Postdoctoral Researcher'), ('employed_phd', 'PhD candidate'), ('associate_scientist', 'Associate Scientist'), ('consultant', 'Consultant'), ('visitor', 'Visitor')], help_text='Select the most suitable category', max_length=64),
+        ),
+    ]
diff --git a/profiles/migrations/0020_auto_20190327_1713.py b/profiles/migrations/0020_auto_20190327_1713.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9d9b127c257ee4098f097bc1ac9dcb57d979ca3
--- /dev/null
+++ b/profiles/migrations/0020_auto_20190327_1713.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2019-03-27 16:13
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('profiles', '0019_auto_20190327_1520'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='affiliation',
+            options={'ordering': ['profile__last_name', 'profile__first_name', '-date_until']},
+        ),
+    ]
diff --git a/profiles/models.py b/profiles/models.py
index fa47729d4545f403ca4f2ce84c053022d3419184..e553074e76e6483586ca83e95113199e16934d38 100644
--- a/profiles/models.py
+++ b/profiles/models.py
@@ -199,8 +199,8 @@ class Affiliation(models.Model):
 
     class Meta:
         default_related_name = 'affiliations'
-        ordering = ['profile__user__last_name', 'profile__user__first_name',
-                    'date_until']
+        ordering = ['profile__last_name', 'profile__first_name',
+                    '-date_until']
 
     def __str__(self):
         return '{ profile }, { organization } [{ date_from } to { date_until }]'.format(
diff --git a/profiles/templates/profiles/affiliation_form.html b/profiles/templates/profiles/affiliation_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..aefa75e6b83bba23ec9df9ebe3f3920dc0fcd408
--- /dev/null
+++ b/profiles/templates/profiles/affiliation_form.html
@@ -0,0 +1,46 @@
+{% extends 'profiles/base.html' %}
+
+{% load bootstrap %}
+
+{% load scipost_extras %}
+
+{% block breadcrumb_items %}
+    {{ block.super }}
+    <span class="breadcrumb-item">{% if form.instance.id %}Update{% else %}Add new{% endif %} Affiliation</span>
+{% endblock %}
+
+{% block pagetitle %}: Affiliation{% endblock pagetitle %}
+
+{% block content %}
+<div class="row">
+  <div class="col-12">
+    <h3 class="highlight">Add a new Affiliation to your Profile</h3>
+    <p class="text-danger">Don't find the organization you need in our list? Please <a href="{% url 'helpdesk:ticket_create' %}">create a Ticket</a> providing us with the details, we'll get back to you!</p>
+    <form action="" method="post">
+      {% csrf_token %}
+      {{ form|bootstrap }}
+
+      {% if form.errors %}
+      {% for field in form %}
+      {% for error in field.errors %}
+      <div class="alert alert-danger">
+	<strong>{{ field.name }} - {{ error|escape }}</strong>
+      </div>
+      {% endfor %}
+      {% endfor %}
+      {% for error in form.non_field_errors %}
+      <div class="alert alert-danger">
+	<strong>{{ error|escape }}</strong>
+      </div>
+      {% endfor %}
+      {% endif %}
+      <input type="submit" value="Submit" class="btn btn-primary">
+    </form>
+  </div>
+</div>
+{% endblock content %}
+
+{% block footer_script %}
+    {{ block.super }}
+    {{ form.media }}
+{% endblock footer_script %}
diff --git a/profiles/urls.py b/profiles/urls.py
index 5fc21faf8468a389094722ae9bd8478528b61237..da204b485c9a871d3640f5d541d18a2bb02ea925 100644
--- a/profiles/urls.py
+++ b/profiles/urls.py
@@ -72,4 +72,9 @@ urlpatterns = [
         views.delete_profile_email,
         name='delete_profile_email'
     ),
+    url(
+        r'^(?P<profile_id>[0-9]+)/affiliation/add/$',
+        views.AffiliationCreateView.as_view(),
+        name='affiliation_create'
+    ),
 ]
diff --git a/profiles/views.py b/profiles/views.py
index 33aee843b4fb8fe2244c4c8ca20c079d7654b5e4..feec4b2c9814aae2a28b26793db5c46647035a57 100644
--- a/profiles/views.py
+++ b/profiles/views.py
@@ -3,6 +3,7 @@ __license__ = "AGPL v3"
 
 
 from django.contrib import messages
+from django.contrib.auth.mixins import UserPassesTestMixin
 from django.core.urlresolvers import reverse, reverse_lazy
 from django.db import transaction
 from django.db.models import Q
@@ -24,8 +25,8 @@ from invitations.models import RegistrationInvitation
 from journals.models import UnregisteredAuthor
 from submissions.models import RefereeInvitation
 
-from .models import Profile, ProfileEmail
-from .forms import ProfileForm, ProfileMergeForm, ProfileEmailForm
+from .models import Profile, ProfileEmail, Affiliation
+from .forms import ProfileForm, ProfileMergeForm, ProfileEmailForm, AffiliationForm
 
 
 
@@ -377,3 +378,37 @@ def delete_profile_email(request, email_id):
     profile_email.delete()
     messages.success(request, 'Email deleted')
     return redirect(profile_email.profile.get_absolute_url())
+
+
+class AffiliationCreateView(UserPassesTestMixin, CreateView):
+    model = Affiliation
+    form_class = AffiliationForm
+    template_name = 'profiles/affiliation_form.html'
+
+    def test_func(self):
+        """
+        Allow creating an Affiliation if user is Admin, EdAdmin or is
+        the Contributor to which this Profile is related.
+        """
+        if self.request.user.has_perm('scipost.can_create_profiles'):
+            return True
+        profile = get_object_or_404(Profile, pk=self.kwargs.get('profile_id'))
+        return self.request.user.contributor.profile is profile
+
+    def get_initial(self, *args, **kwargs):
+        initial = super().get_initial(*args, **kwargs)
+        profile = get_object_or_404(Profile, pk=self.kwargs.get('profile_id'))
+        initial.update({
+            'profile': profile
+        })
+        return initial
+
+    def get_success_url(self):
+        """
+        If request.user is Admin or EdAdmin, redirect to profile detail view.
+        Otherwise if request.user is Profile owner, return to personal page.
+        """
+        if self.request.user.has_perm('scipost.can_create_profiles'):
+            return reverse_lazy('profiles:profile_detail',
+                                kwargs={'pk': self.object.profile.id})
+        return reverse_lazy('scipost:personal_page')
diff --git a/scipost/templates/partials/scipost/personal_page/account.html b/scipost/templates/partials/scipost/personal_page/account.html
index 164aac577ebe00b85d1662a9edd084502dec7aeb..d5f9bad3eae9a5fed2eb1f430cea1afc2bfadced 100644
--- a/scipost/templates/partials/scipost/personal_page/account.html
+++ b/scipost/templates/partials/scipost/personal_page/account.html
@@ -146,6 +146,37 @@
     </div>
 </div>
 
+<div class="row">
+  <div class="col-12">
+    <h3>Your Affiliations:</h3>
+    <ul>
+      <li><a href="{% url 'profiles:affiliation_create' profile_id=contributor.profile.id %}">Add a new Affiliation</a></li>
+    </ul>
+    <table class="table">
+      <thead class="thead-default">
+	<tr>
+	  <th>Organization</th>
+	  <th>Category</th>
+	  <th>From</th>
+	  <th>Until</th>
+	</tr>
+      </thead>
+      <tbody>
+	{% for aff in contributor.profile.affiliations.all %}
+	<tr>
+	  <td>{{ aff.organization }}<br/>&nbsp;{{ aff.description }}</td>
+	  <td>{{ aff.get_category_display }}</td>
+	  <td>{{ aff.date_from|date:'Y-m-d' }}</td>
+	  <td>{{ aff.date_until|date:'Y-m-d' }}</td>
+	</tr>
+	{% empty %}
+	<tr><td colspan="4">No Affiliation has been defined</td></tr>
+	{% endfor %}
+      </tbody>
+    </table>
+  </div>
+</div>
+
 {% if unavailability_form %}
     <hr>
     <div class="row">