Commit 40439efd by Waheed Ahmed

Added Instructor email field and fixed organization.

ECOM-7706
parent 6318abab
......@@ -214,6 +214,7 @@ class PersonSerializer(serializers.ModelSerializer):
profile_image = StdImageSerializerField(required=False)
works = serializers.SlugRelatedField(many=True, read_only=True, slug_field='value', source='person_works')
urls = serializers.SerializerMethodField()
email = serializers.EmailField(required=True)
@classmethod
def prefetch_queryset(cls):
......@@ -225,7 +226,7 @@ class PersonSerializer(serializers.ModelSerializer):
model = Person
fields = (
'uuid', 'given_name', 'family_name', 'bio', 'profile_image_url', 'slug', 'position', 'profile_image',
'partner', 'works', 'urls'
'partner', 'works', 'urls', 'email'
)
extra_kwargs = {
'partner': {'write_only': True}
......
......@@ -1075,6 +1075,7 @@ class PersonSerializerTests(TestCase):
'blog': None
},
'slug': person.slug,
'email': person.email,
}
self.assertDictEqual(serializer.data, expected)
......
......@@ -140,6 +140,7 @@ class PersonViewSetTests(SerializationMixin, APITestCase):
return {
'given_name': "Robert",
'family_name': "Ford",
'email': "test@example.com",
'bio': "The maze is not for him.",
'position': {
'title': "Park Director",
......
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2017-04-27 11:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('course_metadata', '0052_create_course_run_publication_switch'),
]
operations = [
migrations.AddField(
model_name='person',
name='email',
field=models.EmailField(blank=True, max_length=255, null=True),
),
]
......@@ -210,6 +210,7 @@ class Person(TimeStampedModel):
)
slug = AutoSlugField(populate_from=('given_name', 'family_name'), editable=True)
profile_url = models.URLField(null=True, blank=True)
email = models.EmailField(null=True, blank=True, max_length=255)
class Meta:
unique_together = (
......
......@@ -13,7 +13,7 @@ from opaque_keys.edx.keys import CourseKey
from course_discovery.apps.course_metadata.choices import CourseRunPacing
from course_discovery.apps.course_metadata.models import LevelType, Organization, Person, Subject
from course_discovery.apps.ietf_language_tags.models import LanguageTag
from course_discovery.apps.publisher.mixins import LanguageModelSelect2Multiple, check_roles_access
from course_discovery.apps.publisher.mixins import LanguageModelSelect2Multiple, get_user_organizations
from course_discovery.apps.publisher.models import (Course, CourseRun, CourseUserRole, OrganizationExtension,
OrganizationUserRole, PublisherUser, Seat, User)
from course_discovery.apps.publisher.utils import is_internal_user
......@@ -181,17 +181,7 @@ class CustomCourseForm(CourseForm):
).order_by('full_name', 'username')
if user:
organizations = Organization.objects.filter(
organization_extension__organization_id__isnull=False
).order_by(Lower('key'))
if not check_roles_access(user):
# If not internal user return only those organizations which belongs to user.
organizations = organizations.filter(
organization_extension__group__in=user.groups.all()
).order_by(Lower('key'))
self.declared_fields['organization'].queryset = organizations
self.declared_fields['organization'].queryset = get_user_organizations(user)
self.declared_fields['team_admin'].widget.attrs = {'data-user': user.id}
super(CustomCourseForm, self).__init__(*args, **kwargs)
......
......@@ -2,9 +2,11 @@ from functools import wraps
from dal import autocomplete
from django.contrib.auth.decorators import login_required
from django.db.models.functions import Lower
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.utils.decorators import method_decorator
from course_discovery.apps.course_metadata.models import Organization
from course_discovery.apps.publisher.models import Course, Seat
from course_discovery.apps.publisher.utils import is_internal_user, is_publisher_admin, is_publisher_user
......@@ -131,3 +133,25 @@ class LanguageModelSelect2Multiple(autocomplete.ModelSelect2Multiple):
self.choices.queryset = self.choices.queryset.filter(
code__in=[c for c in selected_choices if c]
)
def get_user_organizations(user):
"""
Get organizations for user.
Args:
user (Object): User object
Returns:
Organization (QuerySet): returns Organization objects queryset
"""
organizations = Organization.objects.filter(
organization_extension__organization_id__isnull=False
).order_by(Lower('key'))
if not check_roles_access(user):
# If not internal user return only those organizations which belongs to user.
organizations = organizations.filter(
organization_extension__group__in=user.groups.all()
).order_by(Lower('key'))
return organizations
......@@ -200,6 +200,7 @@ class CourseRunWrapperTests(TestCase):
'social_networks': {},
'bio': staff.bio,
'is_new': True,
'email': staff.email
},
{
'uuid': str(staff_2.uuid),
......@@ -210,7 +211,8 @@ class CourseRunWrapperTests(TestCase):
'profile_url': staff.profile_url,
'is_new': False,
'social_networks': {'facebook': facebook.value, 'twitter': twitter.value},
'bio': staff_2.bio
'bio': staff_2.bio,
'email': staff_2.email
}
]
......
""" Publisher Utils."""
from dateutil import parser
from course_discovery.apps.core.models import User
from course_discovery.apps.publisher.constants import (ADMIN_GROUP_NAME, INTERNAL_USER_GROUP_NAME,
PROJECT_COORDINATOR_GROUP_NAME)
......
......@@ -594,12 +594,14 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix
return reverse(self.success_url, kwargs={'pk': self.object.id})
def get_context_data(self):
user = self.request.user
return {
'course_run': self.get_object(),
'publisher_hide_features_for_pilot': waffle.switch_is_active('publisher_hide_features_for_pilot'),
'publisher_add_instructor_feature': waffle.switch_is_active('publisher_add_instructor_feature'),
'is_internal_user': mixins.check_roles_access(self.request.user),
'is_project_coordinator': is_project_coordinator_user(self.request.user),
'is_internal_user': mixins.check_roles_access(user),
'is_project_coordinator': is_project_coordinator_user(user),
'organizations': mixins.get_user_organizations(user)
}
def get(self, request, *args, **kwargs):
......
......@@ -198,6 +198,7 @@ class CourseRunWrapper(BaseWrapper):
'image_url': staff.get_profile_image_url,
'profile_url': staff.profile_url,
'bio': staff.bio,
'email': staff.email,
'social_networks': {
staff.type: staff.value
for staff in staff.person_networks.all()
......
......@@ -1037,6 +1037,15 @@ msgstr ""
#: templates/publisher/_add_instructor_popup.html
#: templates/publisher/course_run_detail/_instructor_profile.html
msgid "Email"
msgstr ""
#: templates/publisher/_add_instructor_popup.html
msgid "Institution email is preferred"
msgstr ""
#: templates/publisher/_add_instructor_popup.html
#: templates/publisher/course_run_detail/_instructor_profile.html
#: templates/publisher/course_run_detail/_studio.html
#: templates/publisher/dashboard/_in_preview.html
#: templates/publisher/dashboard/_in_progress.html
......
......@@ -1197,6 +1197,15 @@ msgstr "Tïtlé Ⱡ'σяєм ιρѕ#"
#: templates/publisher/_add_instructor_popup.html
#: templates/publisher/course_run_detail/_instructor_profile.html
msgid "Email"
msgstr "Émäïl Ⱡ'σяєм ιρѕ#"
#: templates/publisher/_add_instructor_popup.html
msgid "Institution email is preferred"
msgstr "Ìnstïtütïön émäïl ïs préférréd Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢т#"
#: templates/publisher/_add_instructor_popup.html
#: templates/publisher/course_run_detail/_instructor_profile.html
#: templates/publisher/course_run_detail/_studio.html
#: templates/publisher/dashboard/_in_preview.html
#: templates/publisher/dashboard/_in_progress.html
......
......@@ -34,6 +34,7 @@ $(document).ready(function(){
'given_name': $('#given-name').val(),
'family_name': $('#family-name').val(),
'bio': $('#bio').val(),
'email': $('#email').val(),
'profile_image': $('.select-image').attr('src'),
'position': {
title: $('#title').val(),
......
......@@ -39,6 +39,7 @@ $(document).ready(function(){
$('#instructorProfileModal a.btn-download').attr('href', data['image_url']);
$('#instructorProfileModal div.position').html(data['position']);
$('#instructorProfileModal div.bio').html(data['bio']);
$('#instructorProfileModal div.email').html(data['email']);
assignData('.profile_url', data['profile_url']);
assignData('.facebook_url', data['social_networks']['facebook']);
......
......@@ -159,6 +159,14 @@
.actions {
@include text-align(right);
}
#email {
margin-bottom: 5px;
}
.email-field {
margin-bottom: 20px;
}
}
}
......
......@@ -27,22 +27,30 @@
</label>
<input class="field-input input-text" type="text" id="title" name="title" />
<label class="field-label" for="title">{% trans "Email" %}
<span class="required">* {% trans "required" %}</span>
</label>
<div class="email-field">
<input class="field-input input-text" aria-describedby="email-hint" type="email" id="email" name="email" />
<span id="email-hint">{% trans "Institution email is preferred" %}</span>
</div>
<label class="field-label">{% trans "Organization" %}
<span class="required">* {% trans "required" %}</span>
</label>
{% if edit_mode %}
<span class="read-only-field">{{ organization_name }}</span>
{{ course_form.organization }}
{% if organizations.count > 1 %}
<select class="field-input input-select" id="id_organization" name="organization">
<option value="------">-----</option>
{% for organization in organizations %}
<option value="{{ organization.id }}">{{ organization.name }}</option>
{% endfor%}
</select>
{% else %}
{% if course_form.organization.field.queryset.all.count > 1 %}
{{ course_form.organization }}
{% else %}
{% with course_form.organization.field.queryset|first as organization %}
<span class="read-only-field">{{ organization.name }}</span>
<input id="id_organization" name="organization" type="hidden" value="{{ organization.id }}">
{% endwith %}
{% endif %}
{% with organizations|first as organization %}
<span class="read-only-field">{{ organization.name }}</span>
<input id="id_organization" name="organization" type="hidden" value="{{ organization.id }}">
{% endwith %}
{% endif %}
<label class="field-label" for="bio">{% trans "Bio" %}
......
......@@ -20,6 +20,13 @@
</div>
<div class="info-item">
<div class="heading">{% trans "Email" %}:
{% include "publisher/course_run_detail/_clipboard.html" %}
</div>
<div class="copy email"></div>
</div>
<div class="info-item">
<div class="heading">{% trans "Organization" %}:
{% include "publisher/course_run_detail/_clipboard.html" %}
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment