Commit 0ed3a95d by Andy Armstrong

Merge branch 'release'

Conflicts:
	AUTHORS
	common/static/js/vendor/ova/OpenSeaDragonAnnotation.js
	common/test/acceptance/pages/lms/dashboard.py
parents 2fbf67b2 e22bd1b6
...@@ -174,3 +174,5 @@ Marceau Cnudde <marceau.cnudde@gmail.com> ...@@ -174,3 +174,5 @@ Marceau Cnudde <marceau.cnudde@gmail.com>
Braden MacDonald <mail@bradenm.com> Braden MacDonald <mail@bradenm.com>
Jonathan Piacenti <kelketek@gmail.com> Jonathan Piacenti <kelketek@gmail.com>
Alasdair Swan <aswan@edx.org> Alasdair Swan <aswan@edx.org>
Paul Medlock-Walton <paulmw@mit.edu>
Henry Tareque <henry.tareque@gmail.com>
...@@ -88,7 +88,6 @@ class TestCourseListing(ModuleStoreTestCase): ...@@ -88,7 +88,6 @@ class TestCourseListing(ModuleStoreTestCase):
courses_list = list(get_course_enrollment_pairs(self.student, None, [])) courses_list = list(get_course_enrollment_pairs(self.student, None, []))
self.assertEqual(courses_list, []) self.assertEqual(courses_list, [])
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_course_listing_errored_deleted_courses(self): def test_course_listing_errored_deleted_courses(self):
""" """
Create good courses, courses that won't load, and deleted courses which still have Create good courses, courses that won't load, and deleted courses which still have
......
...@@ -10,6 +10,8 @@ from django.contrib.auth.models import User ...@@ -10,6 +10,8 @@ from django.contrib.auth.models import User
FAKE_MICROSITE = { FAKE_MICROSITE = {
"SITE_NAME": "openedx.localhost", "SITE_NAME": "openedx.localhost",
"university": "fakeuniversity",
"course_org_filter": "fakeorg",
"REGISTRATION_EXTRA_FIELDS": { "REGISTRATION_EXTRA_FIELDS": {
"address1": "required", "address1": "required",
"city": "required", "city": "required",
......
""" """
Student Views Student Views
""" """
import datetime
import logging import logging
import re import re
import uuid import uuid
import time import time
import json import json
from collections import defaultdict from collections import defaultdict
from datetime import datetime
from pytz import UTC from pytz import UTC
from django.conf import settings from django.conf import settings
...@@ -42,7 +42,6 @@ from edxmako.shortcuts import render_to_response, render_to_string ...@@ -42,7 +42,6 @@ from edxmako.shortcuts import render_to_response, render_to_string
from mako.exceptions import TopLevelLookupException from mako.exceptions import TopLevelLookupException
from course_modes.models import CourseMode from course_modes.models import CourseMode
from student.models import ( from student.models import (
Registration, UserProfile, PendingNameChange, Registration, UserProfile, PendingNameChange,
PendingEmailChange, CourseEnrollment, unique_id_for_user, PendingEmailChange, CourseEnrollment, unique_id_for_user,
...@@ -65,7 +64,6 @@ from collections import namedtuple ...@@ -65,7 +64,6 @@ from collections import namedtuple
from courseware.courses import get_courses, sort_by_announcement from courseware.courses import get_courses, sort_by_announcement
from courseware.access import has_access from courseware.access import has_access
from courseware.models import course_modified_times
from django_comment_common.models import Role from django_comment_common.models import Role
...@@ -228,7 +226,7 @@ def single_course_reverification_info(user, course, enrollment): # pylint: disa ...@@ -228,7 +226,7 @@ def single_course_reverification_info(user, course, enrollment): # pylint: disa
ReverifyInfo: (course_id, course_name, course_number, date, status) ReverifyInfo: (course_id, course_name, course_number, date, status)
OR, None: None if there is no re-verification info for this enrollment OR, None: None if there is no re-verification info for this enrollment
""" """
window = MidcourseReverificationWindow.get_window(course.id, datetime.now(UTC)) window = MidcourseReverificationWindow.get_window(course.id, datetime.datetime.now(UTC))
# If there's no window OR the user is not verified, we don't get reverification info # If there's no window OR the user is not verified, we don't get reverification info
if (not window) or (enrollment.mode != "verified"): if (not window) or (enrollment.mode != "verified"):
...@@ -246,8 +244,6 @@ def get_course_enrollment_pairs(user, course_org_filter, org_filter_out_set): ...@@ -246,8 +244,6 @@ def get_course_enrollment_pairs(user, course_org_filter, org_filter_out_set):
Get the relevant set of (Course, CourseEnrollment) pairs to be displayed on Get the relevant set of (Course, CourseEnrollment) pairs to be displayed on
a student's dashboard. a student's dashboard.
""" """
pairs = []
for enrollment in CourseEnrollment.enrollments_for_user(user): for enrollment in CourseEnrollment.enrollments_for_user(user):
course = modulestore().get_course(enrollment.course_id) course = modulestore().get_course(enrollment.course_id)
if course and not isinstance(course, ErrorDescriptor): if course and not isinstance(course, ErrorDescriptor):
...@@ -261,18 +257,12 @@ def get_course_enrollment_pairs(user, course_org_filter, org_filter_out_set): ...@@ -261,18 +257,12 @@ def get_course_enrollment_pairs(user, course_org_filter, org_filter_out_set):
elif course.location.org in org_filter_out_set: elif course.location.org in org_filter_out_set:
continue continue
pairs.append((course, enrollment)) yield (course, enrollment)
else: else:
log.error("User {0} enrolled in {2} course {1}".format( log.error("User {0} enrolled in {2} course {1}".format(
user.username, enrollment.course_id, "broken" if course else "non-existent" user.username, enrollment.course_id, "broken" if course else "non-existent"
)) ))
## Sort pairs in order of courseware access. If I am actively using a course, it should bubble up to the top.
modified_times_map = course_modified_times(user, [p[0].scope_ids.usage_id for p in pairs])
def key_function(x):
return modified_times_map.get(unicode(x[0].scope_ids.usage_id), datetime.min)
pairs.sort(key=key_function, reverse=True)
return pairs
def _cert_info(user, course, cert_status): def _cert_info(user, course, cert_status):
""" """
...@@ -439,7 +429,7 @@ def complete_course_mode_info(course_id, enrollment): ...@@ -439,7 +429,7 @@ def complete_course_mode_info(course_id, enrollment):
mode_info['show_upsell'] = True mode_info['show_upsell'] = True
# if there is an expiration date, find out how long from now it is # if there is an expiration date, find out how long from now it is
if modes['verified'].expiration_datetime: if modes['verified'].expiration_datetime:
today = datetime.now(UTC).date() today = datetime.datetime.now(UTC).date()
mode_info['days_for_upsell'] = (modes['verified'].expiration_datetime.date() - today).days mode_info['days_for_upsell'] = (modes['verified'].expiration_datetime.date() - today).days
return mode_info return mode_info
...@@ -1168,7 +1158,7 @@ def disable_account_ajax(request): ...@@ -1168,7 +1158,7 @@ def disable_account_ajax(request):
context['message'] = _("Unexpected account status") context['message'] = _("Unexpected account status")
return JsonResponse(context, status=400) return JsonResponse(context, status=400)
user_account.changed_by = request.user user_account.changed_by = request.user
user_account.standing_last_changed_at = datetime.now(UTC) user_account.standing_last_changed_at = datetime.datetime.now(UTC)
user_account.save() user_account.save()
return JsonResponse(context) return JsonResponse(context)
...@@ -1553,7 +1543,7 @@ def create_account(request, post_override=None): # pylint: disable-msg=too-many ...@@ -1553,7 +1543,7 @@ def create_account(request, post_override=None): # pylint: disable-msg=too-many
if do_external_auth: if do_external_auth:
eamap.user = new_user eamap.user = new_user
eamap.dtsignup = datetime.now(UTC) eamap.dtsignup = datetime.datetime.now(UTC)
eamap.save() eamap.save()
AUDIT_LOG.info("User registered with external_auth %s", post_vars['username']) AUDIT_LOG.info("User registered with external_auth %s", post_vars['username'])
AUDIT_LOG.info('Updated ExternalAuthMap for %s to be %s', post_vars['username'], eamap) AUDIT_LOG.info('Updated ExternalAuthMap for %s to be %s', post_vars['username'], eamap)
...@@ -1981,7 +1971,7 @@ def confirm_email_change(request, key): # pylint: disable=unused-argument ...@@ -1981,7 +1971,7 @@ def confirm_email_change(request, key): # pylint: disable=unused-argument
meta = u_prof.get_meta() meta = u_prof.get_meta()
if 'old_emails' not in meta: if 'old_emails' not in meta:
meta['old_emails'] = [] meta['old_emails'] = []
meta['old_emails'].append([user.email, datetime.now(UTC).isoformat()]) meta['old_emails'].append([user.email, datetime.datetime.now(UTC).isoformat()])
u_prof.set_meta(meta) u_prof.set_meta(meta)
u_prof.save() u_prof.save()
# Send it to the old email... # Send it to the old email...
...@@ -2101,7 +2091,7 @@ def accept_name_change_by_id(uid): ...@@ -2101,7 +2091,7 @@ def accept_name_change_by_id(uid):
meta = u_prof.get_meta() meta = u_prof.get_meta()
if 'old_names' not in meta: if 'old_names' not in meta:
meta['old_names'] = [] meta['old_names'] = []
meta['old_names'].append([u_prof.name, pnc.rationale, datetime.now(UTC).isoformat()]) meta['old_names'].append([u_prof.name, pnc.rationale, datetime.datetime.now(UTC).isoformat()])
u_prof.set_meta(meta) u_prof.set_meta(meta)
u_prof.name = pnc.new_name u_prof.name = pnc.new_name
......
...@@ -11,6 +11,8 @@ from zendesk import ZendeskError ...@@ -11,6 +11,8 @@ from zendesk import ZendeskError
import json import json
import mock import mock
from student.tests.test_microsite import fake_microsite_get_value
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_FEEDBACK_SUBMISSION": True}) @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_FEEDBACK_SUBMISSION": True})
@override_settings(ZENDESK_URL="dummy", ZENDESK_USER="dummy", ZENDESK_API_KEY="dummy") @override_settings(ZENDESK_URL="dummy", ZENDESK_USER="dummy", ZENDESK_API_KEY="dummy")
...@@ -51,7 +53,7 @@ class SubmitFeedbackTest(TestCase): ...@@ -51,7 +53,7 @@ class SubmitFeedbackTest(TestCase):
HTTP_REFERER="test_referer", HTTP_REFERER="test_referer",
HTTP_USER_AGENT="test_user_agent", HTTP_USER_AGENT="test_user_agent",
REMOTE_ADDR="1.2.3.4", REMOTE_ADDR="1.2.3.4",
SERVER_NAME="test_server" SERVER_NAME="test_server",
) )
req.user = user req.user = user
return views.submit_feedback(req) return views.submit_feedback(req)
...@@ -189,6 +191,49 @@ class SubmitFeedbackTest(TestCase): ...@@ -189,6 +191,49 @@ class SubmitFeedbackTest(TestCase):
self.assertEqual(zendesk_mock_instance.mock_calls, expected_zendesk_calls) self.assertEqual(zendesk_mock_instance.mock_calls, expected_zendesk_calls)
self._assert_datadog_called(datadog_mock, with_tags=True) self._assert_datadog_called(datadog_mock, with_tags=True)
@mock.patch("microsite_configuration.microsite.get_value", fake_microsite_get_value)
def test_valid_request_anon_user_microsite(self, zendesk_mock_class, datadog_mock):
"""
Test a valid request from an anonymous user to a mocked out microsite
The response should have a 200 (success) status code, and a ticket with
the given information should have been submitted via the Zendesk API with the additional
tag that will come from microsite configuration
"""
zendesk_mock_instance = zendesk_mock_class.return_value
zendesk_mock_instance.create_ticket.return_value = 42
self._test_success(self._anon_user, self._anon_fields)
expected_zendesk_calls = [
mock.call.create_ticket(
{
"ticket": {
"requester": {"name": "Test User", "email": "test@edx.org"},
"subject": "a subject",
"comment": {"body": "some details"},
"tags": ["test_course", "test_issue", "LMS", "whitelabel_fakeorg"]
}
}
),
mock.call.update_ticket(
42,
{
"ticket": {
"comment": {
"public": False,
"body":
"Additional information:\n\n"
"Client IP: 1.2.3.4\n"
"Host: test_server\n"
"Page: test_referer\n"
"Browser: test_user_agent"
}
}
}
)
]
self.assertEqual(zendesk_mock_instance.mock_calls, expected_zendesk_calls)
self._assert_datadog_called(datadog_mock, with_tags=True)
def test_bad_request_auth_user_no_subject(self, zendesk_mock_class, datadog_mock): def test_bad_request_auth_user_no_subject(self, zendesk_mock_class, datadog_mock):
"""Test a request from an authenticated user not specifying `subject`.""" """Test a request from an authenticated user not specifying `subject`."""
self._test_bad_request_omit_field(self._auth_user, self._auth_fields, "subject", zendesk_mock_class, datadog_mock) self._test_bad_request_omit_field(self._auth_user, self._auth_fields, "subject", zendesk_mock_class, datadog_mock)
......
...@@ -11,6 +11,7 @@ from django.http import (Http404, HttpResponse, HttpResponseNotAllowed, ...@@ -11,6 +11,7 @@ from django.http import (Http404, HttpResponse, HttpResponseNotAllowed,
from dogapi import dog_stats_api from dogapi import dog_stats_api
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
import zendesk import zendesk
from microsite_configuration import microsite
import calc import calc
import track.views import track.views
...@@ -100,6 +101,13 @@ def _record_feedback_in_zendesk(realname, email, subject, details, tags, additio ...@@ -100,6 +101,13 @@ def _record_feedback_in_zendesk(realname, email, subject, details, tags, additio
# Tag all issues with LMS to distinguish channel in Zendesk; requested by student support team # Tag all issues with LMS to distinguish channel in Zendesk; requested by student support team
zendesk_tags = list(tags.values()) + ["LMS"] zendesk_tags = list(tags.values()) + ["LMS"]
# Per edX support, we would like to be able to route white label feedback items
# via tagging
white_label_org = microsite.get_value('course_org_filter')
if white_label_org:
zendesk_tags = zendesk_tags + ["whitelabel_{org}".format(org=white_label_org)]
new_ticket = { new_ticket = {
"ticket": { "ticket": {
"requester": {"name": realname, "email": email}, "requester": {"name": realname, "email": email},
......
(function () {
'use strict';
/**
* This function will process all the attributes from the DOM element passed, taking all of
* the configuration attributes. It uses the request-username and request-email
* to prompt the user to decide if they want to share their personal information
* with the third party application connecting through LTI.
* @constructor
* @param {jQuery} element DOM element with the lti container.
*/
this.LTI = function (element) {
var dataAttrs = $(element).find('.lti').data(),
askToSendUsername = (dataAttrs.askToSendUsername === 'True'),
askToSendEmail = (dataAttrs.askToSendEmail === 'True');
// When the lti button is clicked, provide users the option to
// accept or reject sending their information to a third party
$(element).on('click', '.link_lti_new_window', function () {
if(askToSendUsername && askToSendEmail) {
return confirm(gettext("Click OK to have your username and e-mail address sent to a 3rd party application.\n\nClick Cancel to return to this page without sending your information."));
} else if (askToSendUsername) {
return confirm(gettext("Click OK to have your username sent to a 3rd party application.\n\nClick Cancel to return to this page without sending your information."));
} else if (askToSendEmail) {
return confirm(gettext("Click OK to have your e-mail address sent to a 3rd party application.\n\nClick Cancel to return to this page without sending your information."));
} else {
return true;
}
});
};
}).call(this);
...@@ -192,6 +192,48 @@ class LTIFields(object): ...@@ -192,6 +192,48 @@ class LTIFields(object):
scope=Scope.settings scope=Scope.settings
) )
# Users will be presented with a message indicating that their e-mail/username would be sent to a third
# party application. When "Open in New Page" is not selected, the tool automatically appears without any user action.
ask_to_send_username = Boolean(
display_name=_("Request user's username"),
# Translators: This is used to request the user's username for a third party service.
# Usernames can only be requested if "Open in New Page" is set to True.
help=_(
"Select True to request the user's username. You must also set Open in New Page to True to get the user's information."
),
default=False,
scope=Scope.settings
)
ask_to_send_email = Boolean(
display_name=_("Request user's email"),
# Translators: This is used to request the user's email for a third party service.
# Emails can only be requested if "Open in New Page" is set to True.
help=_(
"Select True to request the user's email address. You must also set Open in New Page to True to get the user's information."
),
default=False,
scope=Scope.settings
)
description = String(
display_name=_("LTI Application Information"),
help=_(
"Enter a description of the third party application. If requesting username and/or email, use this text box to inform users "
"why their username and/or email will be forwarded to a third party application."
),
default="",
scope=Scope.settings
)
button_text = String(
display_name=_("Button Text"),
help=_(
"Enter the text on the button used to launch the third party application."
),
default="",
scope=Scope.settings
)
class LTIModule(LTIFields, LTI20ModuleMixin, XModule): class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
""" """
...@@ -274,7 +316,13 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule): ...@@ -274,7 +316,13 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
Otherwise error message from LTI provider is generated. Otherwise error message from LTI provider is generated.
""" """
js = {
'js': [
resource_string(__name__, 'js/src/lti/lti.js')
]
}
css = {'scss': [resource_string(__name__, 'css/lti/lti.scss')]} css = {'scss': [resource_string(__name__, 'css/lti/lti.scss')]}
js_module_name = "LTI"
def get_input_fields(self): def get_input_fields(self):
# LTI provides a list of default parameters that might be passed as # LTI provides a list of default parameters that might be passed as
...@@ -317,6 +365,7 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule): ...@@ -317,6 +365,7 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
# parsing custom parameters to dict # parsing custom parameters to dict
custom_parameters = {} custom_parameters = {}
for custom_parameter in self.custom_parameters: for custom_parameter in self.custom_parameters:
try: try:
param_name, param_value = [p.strip() for p in custom_parameter.split('=', 1)] param_name, param_value = [p.strip() for p in custom_parameter.split('=', 1)]
...@@ -370,6 +419,11 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule): ...@@ -370,6 +419,11 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
'weight': self.weight, 'weight': self.weight,
'module_score': self.module_score, 'module_score': self.module_score,
'comment': sanitized_comment, 'comment': sanitized_comment,
'description': self.description,
'ask_to_send_username': self.ask_to_send_username,
'ask_to_send_email': self.ask_to_send_email,
'button_text': self.button_text,
} }
def get_html(self): def get_html(self):
...@@ -516,6 +570,29 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule): ...@@ -516,6 +570,29 @@ class LTIModule(LTIFields, LTI20ModuleMixin, XModule):
u'lis_outcome_service_url': self.get_outcome_service_url() u'lis_outcome_service_url': self.get_outcome_service_url()
}) })
self.user_email = ""
self.user_username = ""
# Username and email can't be sent in studio mode, because the user object is not defined.
# To test functionality test in LMS
if callable(self.runtime.get_real_user):
real_user_object = self.runtime.get_real_user(self.runtime.anonymous_student_id)
try:
self.user_email = real_user_object.email
except AttributeError:
self.user_email = ""
try:
self.user_username = real_user_object.username
except AttributeError:
self.user_username = ""
if self.open_in_a_new_page:
if self.ask_to_send_username and self.user_username:
body["lis_person_sourcedid"] = self.user_username
if self.ask_to_send_email and self.user_email:
body["lis_person_contact_email_primary"] = self.user_email
# Appending custom parameter for signing. # Appending custom parameter for signing.
body.update(custom_parameters) body.update(custom_parameters)
......
...@@ -584,7 +584,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ...@@ -584,7 +584,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
var targetThumb = an.target.thumb; var targetThumb = an.target.thumb;
if (isContainer) { if (isContainer) {
isSource = (targetThumb.indexOf(sourceId) !== -1); isSource = (targetThumb.indexOf(sourceId) !== -1);
} }
return (isOpenSeaDragon && isContainer && isImage && isRP && isSource); return (isOpenSeaDragon && isContainer && isImage && isRP && isSource);
}, },
......
...@@ -20,13 +20,13 @@ class DashboardPage(PageObject): ...@@ -20,13 +20,13 @@ class DashboardPage(PageObject):
return self.q(css='section.my-courses').present return self.q(css='section.my-courses').present
@property @property
def courses_text(self): def current_courses_text(self):
""" """
This is the title label for the section of the student dashboard that This is the title label for the section of the student dashboard that
shows all the courses that the student is enrolled in. shows all the courses that the student is enrolled in.
The string displayed is defined in lms/templates/dashboard.html. The string displayed is defined in lms/templates/dashboard.html.
""" """
text_items = self.q(css='section#my-courses span.my-courses-title-label').text text_items = self.q(css='section#my-courses').text
if len(text_items) > 0: if len(text_items) > 0:
return text_items[0] return text_items[0]
else: else:
......
...@@ -78,10 +78,10 @@ class LanguageTest(WebAppTest): ...@@ -78,10 +78,10 @@ class LanguageTest(WebAppTest):
self.dashboard_page = DashboardPage(self.browser) self.dashboard_page = DashboardPage(self.browser)
self.test_new_lang = 'eo' self.test_new_lang = 'eo'
# This string is unicode for "ÇØÜRSÉS", which should appear in our Dummy Esperanto page # This string is unicode for "ÇÜRRÉNT ÇØÜRSÉS", which should appear in our Dummy Esperanto page
# We store the string this way because Selenium seems to try and read in strings from # We store the string this way because Selenium seems to try and read in strings from
# the HTML in this format. Ideally we could just store the raw ÇÜRRÉNT ÇØÜRSÉS string here # the HTML in this format. Ideally we could just store the raw ÇÜRRÉNT ÇØÜRSÉS string here
self.courses_text = u'\xc7\xd6\xdcRS\xc9S' self.current_courses_text = u'\xc7\xdcRR\xc9NT \xc7\xd6\xdcRS\xc9S'
self.username = "test" self.username = "test"
self.password = "testpass" self.password = "testpass"
...@@ -93,10 +93,10 @@ class LanguageTest(WebAppTest): ...@@ -93,10 +93,10 @@ class LanguageTest(WebAppTest):
# Change language to Dummy Esperanto # Change language to Dummy Esperanto
self.dashboard_page.change_language(self.test_new_lang) self.dashboard_page.change_language(self.test_new_lang)
changed_text = self.dashboard_page.courses_text changed_text = self.dashboard_page.current_courses_text
# We should see the dummy-language text on the page # We should see the dummy-language text on the page
self.assertIn(self.courses_text, changed_text) self.assertIn(self.current_courses_text, changed_text)
def test_language_persists(self): def test_language_persists(self):
auto_auth_page = AutoAuthPage(self.browser, username=self.username, password=self.password, email=self.email) auto_auth_page = AutoAuthPage(self.browser, username=self.username, password=self.password, email=self.email)
...@@ -114,10 +114,10 @@ class LanguageTest(WebAppTest): ...@@ -114,10 +114,10 @@ class LanguageTest(WebAppTest):
self.dashboard_page.visit() self.dashboard_page.visit()
changed_text = self.dashboard_page.courses_text changed_text = self.dashboard_page.current_courses_text
# We should see the dummy-language text on the page # We should see the dummy-language text on the page
self.assertIn(self.courses_text, changed_text) self.assertIn(self.current_courses_text, changed_text)
class HighLevelTabTest(UniqueCourseTest): class HighLevelTabTest(UniqueCourseTest):
......
...@@ -26,7 +26,7 @@ msgid "" ...@@ -26,7 +26,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Amharic (http://www.transifex.com/projects/p/edx-platform/language/am/)\n" "Language-Team: Amharic (http://www.transifex.com/projects/p/edx-platform/language/am/)\n"
......
...@@ -64,7 +64,7 @@ msgid "" ...@@ -64,7 +64,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-09-22 06:00+0000\n" "PO-Revision-Date: 2014-09-22 06:00+0000\n"
"Last-Translator: Saramami.mami <saramami.mami@yahoo.com>\n" "Last-Translator: Saramami.mami <saramami.mami@yahoo.com>\n"
"Language-Team: Arabic (http://www.transifex.com/projects/p/edx-platform/language/ar/)\n" "Language-Team: Arabic (http://www.transifex.com/projects/p/edx-platform/language/ar/)\n"
......
...@@ -26,7 +26,7 @@ msgid "" ...@@ -26,7 +26,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Azerbaijani (http://www.transifex.com/projects/p/edx-platform/language/az/)\n" "Language-Team: Azerbaijani (http://www.transifex.com/projects/p/edx-platform/language/az/)\n"
......
...@@ -26,7 +26,7 @@ msgid "" ...@@ -26,7 +26,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Bulgarian (Bulgaria) (http://www.transifex.com/projects/p/edx-platform/language/bg_BG/)\n" "Language-Team: Bulgarian (Bulgaria) (http://www.transifex.com/projects/p/edx-platform/language/bg_BG/)\n"
......
...@@ -27,7 +27,7 @@ msgid "" ...@@ -27,7 +27,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Bengali (Bangladesh) (http://www.transifex.com/projects/p/edx-platform/language/bn_BD/)\n" "Language-Team: Bengali (Bangladesh) (http://www.transifex.com/projects/p/edx-platform/language/bn_BD/)\n"
......
...@@ -26,7 +26,7 @@ msgid "" ...@@ -26,7 +26,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Bengali (India) (http://www.transifex.com/projects/p/edx-platform/language/bn_IN/)\n" "Language-Team: Bengali (India) (http://www.transifex.com/projects/p/edx-platform/language/bn_IN/)\n"
......
...@@ -28,7 +28,7 @@ msgid "" ...@@ -28,7 +28,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Bosnian (http://www.transifex.com/projects/p/edx-platform/language/bs/)\n" "Language-Team: Bosnian (http://www.transifex.com/projects/p/edx-platform/language/bs/)\n"
......
...@@ -30,7 +30,7 @@ msgid "" ...@@ -30,7 +30,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Catalan (http://www.transifex.com/projects/p/edx-platform/language/ca/)\n" "Language-Team: Catalan (http://www.transifex.com/projects/p/edx-platform/language/ca/)\n"
......
...@@ -27,7 +27,7 @@ msgid "" ...@@ -27,7 +27,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Catalan (Valencian) (http://www.transifex.com/projects/p/edx-platform/language/ca@valencia/)\n" "Language-Team: Catalan (Valencian) (http://www.transifex.com/projects/p/edx-platform/language/ca@valencia/)\n"
......
...@@ -35,7 +35,7 @@ msgid "" ...@@ -35,7 +35,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Czech (http://www.transifex.com/projects/p/edx-platform/language/cs/)\n" "Language-Team: Czech (http://www.transifex.com/projects/p/edx-platform/language/cs/)\n"
......
...@@ -26,7 +26,7 @@ msgid "" ...@@ -26,7 +26,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Welsh (http://www.transifex.com/projects/p/edx-platform/language/cy/)\n" "Language-Team: Welsh (http://www.transifex.com/projects/p/edx-platform/language/cy/)\n"
......
...@@ -26,7 +26,7 @@ msgid "" ...@@ -26,7 +26,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Danish (http://www.transifex.com/projects/p/edx-platform/language/da/)\n" "Language-Team: Danish (http://www.transifex.com/projects/p/edx-platform/language/da/)\n"
......
...@@ -57,7 +57,7 @@ msgid "" ...@@ -57,7 +57,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-09-10 14:00+0000\n" "PO-Revision-Date: 2014-09-10 14:00+0000\n"
"Last-Translator: Alexander L. <alexander.lohberg@paluno.uni-due.de>\n" "Last-Translator: Alexander L. <alexander.lohberg@paluno.uni-due.de>\n"
"Language-Team: German (Germany) (http://www.transifex.com/projects/p/edx-platform/language/de_DE/)\n" "Language-Team: German (Germany) (http://www.transifex.com/projects/p/edx-platform/language/de_DE/)\n"
......
...@@ -35,7 +35,7 @@ msgid "" ...@@ -35,7 +35,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: edx-platform\n" "Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2014-09-23 15:15-0400\n" "POT-Creation-Date: 2014-09-24 13:34-0400\n"
"PO-Revision-Date: 2014-08-28 00:15+0000\n" "PO-Revision-Date: 2014-08-28 00:15+0000\n"
"Last-Translator: Sarina Canelake <sarina@edx.org>\n" "Last-Translator: Sarina Canelake <sarina@edx.org>\n"
"Language-Team: Greek (http://www.transifex.com/projects/p/edx-platform/language/el/)\n" "Language-Team: Greek (http://www.transifex.com/projects/p/edx-platform/language/el/)\n"
......
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