Commit 0db8bac7 by Christina Roberts

Merge pull request #8331 from edx/diana/teams-tab

Add teams tab
parents 348878be f70891b5
/**
* @license RequireJS text 2.0.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/requirejs/text for details
*/
/*jslint regexp: true */
/*global require, XMLHttpRequest, ActiveXObject,
define, window, process, Packages,
java, location, Components, FileUtils */
// Added by edX: we namespace requirejs and its associated functions.
var namespaced_define = window.define !== undefined ? define : RequireJS.define;
namespaced_define(['module'], function (module) {
'use strict';
var text, fs, Cc, Ci, xpcIsWindows,
progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
hasLocation = typeof location !== 'undefined' && location.href,
defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
defaultHostName = hasLocation && location.hostname,
defaultPort = hasLocation && (location.port || undefined),
buildMap = {},
masterConfig = (module.config && module.config()) || {};
text = {
version: '2.0.14',
strip: function (content) {
//Strips <?xml ...?> declarations so that external SVG and XML
//documents can be added to a document without worry. Also, if the string
//is an HTML document, only the part inside the body tag is returned.
if (content) {
content = content.replace(xmlRegExp, "");
var matches = content.match(bodyRegExp);
if (matches) {
content = matches[1];
}
} else {
content = "";
}
return content;
},
jsEscape: function (content) {
return content.replace(/(['\\])/g, '\\$1')
.replace(/[\f]/g, "\\f")
.replace(/[\b]/g, "\\b")
.replace(/[\n]/g, "\\n")
.replace(/[\t]/g, "\\t")
.replace(/[\r]/g, "\\r")
.replace(/[\u2028]/g, "\\u2028")
.replace(/[\u2029]/g, "\\u2029");
},
createXhr: masterConfig.createXhr || function () {
//Would love to dump the ActiveX crap in here. Need IE 6 to die first.
var xhr, i, progId;
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else if (typeof ActiveXObject !== "undefined") {
for (i = 0; i < 3; i += 1) {
progId = progIds[i];
try {
xhr = new ActiveXObject(progId);
} catch (e) {}
if (xhr) {
progIds = [progId]; // so faster next time
break;
}
}
}
return xhr;
},
/**
* Parses a resource name into its component parts. Resource names
* look like: module/name.ext!strip, where the !strip part is
* optional.
* @param {String} name the resource name
* @returns {Object} with properties "moduleName", "ext" and "strip"
* where strip is a boolean.
*/
parseName: function (name) {
var modName, ext, temp,
strip = false,
index = name.lastIndexOf("."),
isRelative = name.indexOf('./') === 0 ||
name.indexOf('../') === 0;
if (index !== -1 && (!isRelative || index > 1)) {
modName = name.substring(0, index);
ext = name.substring(index + 1);
} else {
modName = name;
}
temp = ext || modName;
index = temp.indexOf("!");
if (index !== -1) {
//Pull off the strip arg.
strip = temp.substring(index + 1) === "strip";
temp = temp.substring(0, index);
if (ext) {
ext = temp;
} else {
modName = temp;
}
}
return {
moduleName: modName,
ext: ext,
strip: strip
};
},
xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,
/**
* Is an URL on another domain. Only works for browser use, returns
* false in non-browser environments. Only used to know if an
* optimized .js version of a text resource should be loaded
* instead.
* @param {String} url
* @returns Boolean
*/
useXhr: function (url, protocol, hostname, port) {
var uProtocol, uHostName, uPort,
match = text.xdRegExp.exec(url);
if (!match) {
return true;
}
uProtocol = match[2];
uHostName = match[3];
uHostName = uHostName.split(':');
uPort = uHostName[1];
uHostName = uHostName[0];
return (!uProtocol || uProtocol === protocol) &&
(!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
((!uPort && !uHostName) || uPort === port);
},
finishLoad: function (name, strip, content, onLoad) {
content = strip ? text.strip(content) : content;
if (masterConfig.isBuild) {
buildMap[name] = content;
}
onLoad(content);
},
load: function (name, req, onLoad, config) {
//Name has format: some.module.filext!strip
//The strip part is optional.
//if strip is present, then that means only get the string contents
//inside a body tag in an HTML string. For XML/SVG content it means
//removing the <?xml ...?> declarations so the content can be inserted
//into the current doc without problems.
// Do not bother with the work if a build and text will
// not be inlined.
if (config && config.isBuild && !config.inlineText) {
onLoad();
return;
}
masterConfig.isBuild = config && config.isBuild;
var parsed = text.parseName(name),
nonStripName = parsed.moduleName +
(parsed.ext ? '.' + parsed.ext : ''),
url = req.toUrl(nonStripName),
useXhr = (masterConfig.useXhr) ||
text.useXhr;
// Do not load if it is an empty: url
if (url.indexOf('empty:') === 0) {
onLoad();
return;
}
//Load the text. Use XHR if possible and in a browser.
if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
text.get(url, function (content) {
text.finishLoad(name, parsed.strip, content, onLoad);
}, function (err) {
if (onLoad.error) {
onLoad.error(err);
}
});
} else {
//Need to fetch the resource across domains. Assume
//the resource has been optimized into a JS module. Fetch
//by the module name + extension, but do not include the
//!strip part to avoid file system issues.
req([nonStripName], function (content) {
text.finishLoad(parsed.moduleName + '.' + parsed.ext,
parsed.strip, content, onLoad);
});
}
},
write: function (pluginName, moduleName, write, config) {
if (buildMap.hasOwnProperty(moduleName)) {
var content = text.jsEscape(buildMap[moduleName]);
write.asModule(pluginName + "!" + moduleName,
"namespaced_define(function () { return '" +
content +
"';});\n");
}
},
writeFile: function (pluginName, moduleName, req, write, config) {
var parsed = text.parseName(moduleName),
extPart = parsed.ext ? '.' + parsed.ext : '',
nonStripName = parsed.moduleName + extPart,
//Use a '.js' file name so that it indicates it is a
//script that can be loaded across domains.
fileName = req.toUrl(parsed.moduleName + extPart) + '.js';
//Leverage own load() method to load plugin value, but only
//write out values that do not have the strip argument,
//to avoid any potential issues with ! in file names.
text.load(nonStripName, req, function (value) {
//Use own write() method to construct full module value.
//But need to create shell that translates writeFile's
//write() to the right interface.
var textWrite = function (contents) {
return write(fileName, contents);
};
textWrite.asModule = function (moduleName, contents) {
return write.asModule(moduleName, fileName, contents);
};
text.write(pluginName, nonStripName, textWrite, config);
}, config);
}
};
if (masterConfig.env === 'node' || (!masterConfig.env &&
typeof process !== "undefined" &&
process.versions &&
!!process.versions.node &&
!process.versions['node-webkit'] &&
!process.versions['atom-shell'])) {
//Using special require.nodeRequire, something added by r.js.
fs = require.nodeRequire('fs');
text.get = function (url, callback, errback) {
try {
var file = fs.readFileSync(url, 'utf8');
//Remove BOM (Byte Mark Order) from utf8 files if it is there.
if (file[0] === '\uFEFF') {
file = file.substring(1);
}
callback(file);
} catch (e) {
if (errback) {
errback(e);
}
}
};
} else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
text.createXhr())) {
text.get = function (url, callback, errback, headers) {
var xhr = text.createXhr(), header;
xhr.open('GET', url, true);
//Allow plugins direct access to xhr headers
if (headers) {
for (header in headers) {
if (headers.hasOwnProperty(header)) {
xhr.setRequestHeader(header.toLowerCase(), headers[header]);
}
}
}
//Allow overrides specified in config
if (masterConfig.onXhr) {
masterConfig.onXhr(xhr, url);
}
xhr.onreadystatechange = function (evt) {
var status, err;
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if (xhr.readyState === 4) {
status = xhr.status || 0;
if (status > 399 && status < 600) {
//An http 4xx or 5xx error. Signal an error.
err = new Error(url + ' HTTP status: ' + status);
err.xhr = xhr;
if (errback) {
errback(err);
}
} else {
callback(xhr.responseText);
}
if (masterConfig.onXhrComplete) {
masterConfig.onXhrComplete(xhr, url);
}
}
};
xhr.send(null);
};
} else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
//Why Java, why is this so awkward?
text.get = function (url, callback) {
var stringBuffer, line,
encoding = "utf-8",
file = new java.io.File(url),
lineSeparator = java.lang.System.getProperty("line.separator"),
input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
content = '';
try {
stringBuffer = new java.lang.StringBuffer();
line = input.readLine();
// Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
// http://www.unicode.org/faq/utf_bom.html
// Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
if (line && line.length() && line.charAt(0) === 0xfeff) {
// Eat the BOM, since we've already found the encoding on this file,
// and we plan to concatenating this buffer with others; the BOM should
// only appear at the top of a file.
line = line.substring(1);
}
if (line !== null) {
stringBuffer.append(line);
}
while ((line = input.readLine()) !== null) {
stringBuffer.append(lineSeparator);
stringBuffer.append(line);
}
//Make sure we return a JavaScript string and not a Java string.
content = String(stringBuffer.toString()); //String
} finally {
input.close();
}
callback(content);
};
} else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
typeof Components !== 'undefined' && Components.classes &&
Components.interfaces)) {
//Avert your gaze!
Cc = Components.classes;
Ci = Components.interfaces;
Components.utils['import']('resource://gre/modules/FileUtils.jsm');
xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);
text.get = function (url, callback) {
var inStream, convertStream, fileObj,
readData = {};
if (xpcIsWindows) {
url = url.replace(/\//g, '\\');
}
fileObj = new FileUtils.File(url);
//XPCOM, you so crazy
try {
inStream = Cc['@mozilla.org/network/file-input-stream;1']
.createInstance(Ci.nsIFileInputStream);
inStream.init(fileObj, 1, 0, false);
convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
.createInstance(Ci.nsIConverterInputStream);
convertStream.init(inStream, "utf-8", inStream.available(),
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
convertStream.readString(inStream.available(), readData);
convertStream.close();
inStream.close();
callback(readData.value);
} catch (e) {
throw new Error((fileObj && fileObj.path || '') + ': ' + e);
}
};
}
return text;
});
# -*- coding: utf-8 -*-
"""
Teams page.
"""
from .course_page import CoursePage
class TeamsPage(CoursePage):
"""
Teams page/tab.
"""
url_path = "teams"
def is_browser_on_page(self):
""" Checks if teams page is being viewed """
return self.q(css='body.view-teams').present
def get_body_text(self):
""" Returns the current dummy text. This will be changed once there is more content on the page. """
return self.q(css='.teams-text').text[0]
"""
Acceptance tests for the teams feature.
"""
from ..helpers import UniqueCourseTest
from ...pages.lms.teams import TeamsPage
from nose.plugins.attrib import attr
from ...fixtures.course import CourseFixture
from ...pages.lms.tab_nav import TabNavPage
from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.course_info import CourseInfoPage
@attr('shard_5')
class TeamsTabTest(UniqueCourseTest):
"""
Tests verifying when the Teams tab is present.
"""
def setUp(self):
super(TeamsTabTest, self).setUp()
self.tab_nav = TabNavPage(self.browser)
self.course_info_page = CourseInfoPage(self.browser, self.course_id)
self.teams_page = TeamsPage(self.browser, self.course_id)
self.test_topic = {u"name": u"a topic", u"description": u"test topic", u"id": 0}
def set_team_configuration(self, configuration, enroll_in_course=True, global_staff=False):
"""
Sets team configuration on the course and calls auto-auth on the user.
"""
#pylint: disable=attribute-defined-outside-init
self.course_fixture = CourseFixture(**self.course_info)
if configuration:
self.course_fixture.add_advanced_settings(
{u"teams_configuration": {u"value": configuration}}
)
self.course_fixture.install()
enroll_course_id = self.course_id if enroll_in_course else None
AutoAuthPage(self.browser, course_id=enroll_course_id, staff=global_staff).visit()
self.course_info_page.visit()
def verify_teams_present(self, present):
"""
Verifies whether or not the teams tab is present. If it should be present, also
checks the text on the page (to ensure view is working).
"""
if present:
self.assertIn("Teams", self.tab_nav.tab_names)
self.teams_page.visit()
self.assertEqual("This is the new Teams tab.", self.teams_page.get_body_text())
else:
self.assertNotIn("Teams", self.tab_nav.tab_names)
def test_teams_not_enabled(self):
"""
Scenario: teams tab should not be present if no team configuration is set
Given I am enrolled in a course without team configuration
When I view the course info page
Then I should not see the Teams tab
"""
self.set_team_configuration(None)
self.verify_teams_present(False)
def test_teams_not_enabled_no_topics(self):
"""
Scenario: teams tab should not be present if team configuration does not specify topics
Given I am enrolled in a course with no topics in the team configuration
When I view the course info page
Then I should not see the Teams tab
"""
self.set_team_configuration({u"max_team_size": 10, u"topics": []})
self.verify_teams_present(False)
def test_teams_not_enabled_not_enrolled(self):
"""
Scenario: teams tab should not be present if student is not enrolled in the course
Given there is a course with team configuration and topics
And I am not enrolled in that course, and am not global staff
When I view the course info page
Then I should not see the Teams tab
"""
self.set_team_configuration({u"max_team_size": 10, u"topics": [self.test_topic]}, enroll_in_course=False)
self.verify_teams_present(False)
def test_teams_enabled(self):
"""
Scenario: teams tab should be present if user is enrolled in the course and it has team configuration
Given I am enrolled in a course with team configuration and topics
When I view the course info page
Then I should see the Teams tab
And the correct content should be on the page
"""
self.set_team_configuration({u"max_team_size": 10, u"topics": [self.test_topic]})
self.verify_teams_present(True)
def test_teams_enabled_global_staff(self):
"""
Scenario: teams tab should be present if user is not enrolled in the course, but is global staff
Given there is a course with team configuration
And I am not enrolled in that course, but am global staff
When I view the course info page
Then I should see the Teams tab
And the correct content should be on the page
"""
self.set_team_configuration(
{u"max_team_size": 10, u"topics": [self.test_topic]}, enroll_in_course=False, global_staff=True
)
self.verify_teams_present(True)
"""
Definition of the course team feature.
"""
from django.utils.translation import ugettext as _
from courseware.tabs import EnrolledCourseViewType
from .views import is_feature_enabled
class TeamsCourseViewType(EnrolledCourseViewType):
"""
The representation of the course teams view type.
"""
name = "teams"
title = _("Teams")
view_name = "teams_dashboard"
@classmethod
def is_enabled(cls, course, user=None):
"""Returns true if the teams feature is enabled in the course.
Args:
course (CourseDescriptor): the course using the feature
user (User): the user interacting with the course
"""
if not super(TeamsCourseViewType, cls).is_enabled(course, user=user):
return False
return is_feature_enabled(course)
define(["jquery", "teams/js/teams_tab_factory"],
function($, TeamsTabFactory) {
'use strict';
describe("teams django app", function() {
var teamsTab;
beforeEach(function() {
setFixtures("<div class='team-tab-content'></div>");
teamsTab = new TeamsTabFactory();
});
it("can load templates", function() {
expect($("body").text()).toContain("This is the new Teams tab");
});
});
}
);
;(function (define) {
'use strict';
define(['jquery', 'teams/js/views/teams_tab'],
function ($, TeamsTabView) {
return function () {
var view = new TeamsTabView({
el: $('.team-tab-content')
});
view.render();
};
});
}).call(this, define || RequireJS.define);
;(function (define) {
'use strict';
define(['backbone', 'underscore', 'text!teams/templates/teams-tab.underscore'],
function (Backbone, _, teamsTabTemplate) {
var TeamTabView = Backbone.View.extend({
render: function() {
this.$el.html(_.template(teamsTabTemplate, {}));
}
});
return TeamTabView;
});
}).call(this, define || RequireJS.define);
<p class="teams-text">This is the new Teams tab.</p>
## mako
<%! from django.utils.translation import ugettext as _ %>
<%namespace name='static' file='/static_content.html'/>
<%inherit file="/main.html" />
<%block name="bodyclass">view-teams is-in-course course</%block>
<%block name="pagetitle">${_("Teams")}</%block>
<%block name="headextra">
<%static:css group='style-course'/>
</%block>
<%include file="/courseware/course_navigation.html" args="active_page='teams'" />
<div class="team-tab-content"></div>
<%block name="js_extra">
<script type="text/javascript">
(function (require) {
require(['teams/js/teams_tab_factory'], function (TeamsTabFactory) {
var pageView = new TeamsTabFactory({
});
});
}).call(this, require || RequireJS.require);
</script>
</%block>
"""
Tests for views.py
"""
from nose.plugins.attrib import attr
from student.tests.factories import (
CourseEnrollmentFactory,
UserFactory,
)
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from django.http import Http404
from django.core.urlresolvers import reverse
from rest_framework.test import APIClient
@attr('shard_1')
class TestDashboard(ModuleStoreTestCase):
test_password = "test"
def setUp(self):
"""
Set up tests
"""
super(TestDashboard, self).setUp()
self.course = CourseFactory.create(
teams_configuration={"max_team_size": 10, "topics": [{"name": "foo", "id": 0, "description": "test topic"}]}
)
# will be assigned to self.client by default
self.user = UserFactory.create(password=self.test_password)
self.teams_url = reverse('teams_dashboard', args=[self.course.id])
def test_anonymous(self):
""" Verifies that an anonymous client cannot access the team dashboard. """
anonymous_client = APIClient()
response = anonymous_client.get(self.teams_url)
self.assertEqual(404, response.status_code)
def test_not_enrolled_not_staff(self):
""" Verifies that a student who is not enrolled cannot access the team dashboard. """
response = self.client.get(self.teams_url)
self.assertEqual(404, response.status_code)
def test_not_enrolled_staff(self):
"""
Verifies that a user with global access who is not enrolled in the course can access the team dashboard.
"""
staff_user = UserFactory(is_staff=True, password=self.test_password)
staff_client = APIClient()
staff_client.login(username=staff_user.username, password=self.test_password)
response = staff_client.get(self.teams_url)
self.assertContains(response, "TeamsTabFactory", status_code=200)
def test_enrolled_not_staff(self):
"""
Verifies that a user without global access who is enrolled in the course can access the team dashboard.
"""
CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
self.client.login(username=self.user.username, password=self.test_password)
response = self.client.get(self.teams_url)
self.assertContains(response, "TeamsTabFactory", status_code=200)
def test_enrolled_teams_not_enabled(self):
"""
Verifies that a user without global access who is enrolled in the course cannot access the team dashboard
if the teams feature is not enabled.
"""
course = CourseFactory.create()
teams_url = reverse('teams_dashboard', args=[course.id])
CourseEnrollmentFactory.create(user=self.user, course_id=course.id)
self.client.login(username=self.user.username, password=self.test_password)
response = self.client.get(teams_url)
self.assertEqual(404, response.status_code)
def test_bad_course_id(self):
"""
Verifies expected behavior when course_id does not reference an existing course or is invalid.
"""
bad_org = "badorgxxx"
bad_team_url = self.teams_url.replace(self.course.id.org, bad_org)
response = self.client.get(bad_team_url)
self.assertEqual(404, response.status_code)
bad_team_url = bad_team_url.replace(bad_org, "invalid/course/id")
response = self.client.get(bad_team_url)
self.assertEqual(404, response.status_code)
"""
URLs for teams.
"""
from django.conf.urls import patterns, url
from teams.views import TeamsDashboardView
urlpatterns = patterns(
"teams.views",
url(r"^/$", TeamsDashboardView.as_view(), name="teams_dashboard"),
)
"""
View methods for the course team feature.
"""
from django.shortcuts import render_to_response
from opaque_keys.edx.keys import CourseKey
from courseware.courses import get_course_with_access, has_access
from django.http import Http404
from django.conf import settings
from django.views.generic.base import View
from student.models import CourseEnrollment
class TeamsDashboardView(View):
"""
View methods related to the teams dashboard.
"""
def get(self, request, course_id):
"""
Renders the teams dashboard, which is shown on the "Teams" tab.
Raises a 404 if the course specified by course_id does not exist, the
user is not registered for the course, or the teams feature is not enabled.
"""
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, "load", course_key)
if not is_feature_enabled(course):
raise Http404
if not CourseEnrollment.is_enrolled(request.user, course.id) and \
not has_access(request.user, 'staff', course, course.id):
raise Http404
context = {"course": course}
return render_to_response("teams/teams.html", context)
def is_feature_enabled(course):
"""
Returns True if the teams feature is enabled.
"""
return settings.FEATURES.get('ENABLE_TEAMS', False) and course.teams_enabled
...@@ -1854,6 +1854,9 @@ INSTALLED_APPS = ( ...@@ -1854,6 +1854,9 @@ INSTALLED_APPS = (
# Credit courses # Credit courses
'openedx.core.djangoapps.credit', 'openedx.core.djangoapps.credit',
# Course teams
'teams',
) )
######################### CSRF ######################################### ######################### CSRF #########################################
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
'jquery.url': 'xmodule_js/common_static/js/vendor/url.min', 'jquery.url': 'xmodule_js/common_static/js/vendor/url.min',
'datepair': 'xmodule_js/common_static/js/vendor/timepicker/datepair', 'datepair': 'xmodule_js/common_static/js/vendor/timepicker/datepair',
'date': 'xmodule_js/common_static/js/vendor/date', 'date': 'xmodule_js/common_static/js/vendor/date',
'text': 'xmodule_js/common_static/js/vendor/requirejs/text',
'underscore': 'xmodule_js/common_static/js/vendor/underscore-min', 'underscore': 'xmodule_js/common_static/js/vendor/underscore-min',
'underscore.string': 'xmodule_js/common_static/js/vendor/underscore.string.min', 'underscore.string': 'xmodule_js/common_static/js/vendor/underscore.string.min',
'backbone': 'xmodule_js/common_static/js/vendor/backbone-min', 'backbone': 'xmodule_js/common_static/js/vendor/backbone-min',
...@@ -576,6 +577,7 @@ ...@@ -576,6 +577,7 @@
// TODO: why do these need 'lms/include' at the front but the CMS equivalent logic doesn't? // TODO: why do these need 'lms/include' at the front but the CMS equivalent logic doesn't?
define([ define([
// Run the LMS tests // Run the LMS tests
'lms/include/teams/js/spec/teams_factory_spec.js',
'lms/include/js/spec/photocapture_spec.js', 'lms/include/js/spec/photocapture_spec.js',
'lms/include/js/spec/staff_debug_actions_spec.js', 'lms/include/js/spec/staff_debug_actions_spec.js',
'lms/include/js/spec/views/notification_spec.js', 'lms/include/js/spec/views/notification_spec.js',
......
...@@ -35,6 +35,7 @@ lib_paths: ...@@ -35,6 +35,7 @@ lib_paths:
- xmodule_js/common_static/js/vendor/jasmine-imagediff.js - xmodule_js/common_static/js/vendor/jasmine-imagediff.js
- xmodule_js/common_static/js/vendor/require.js - xmodule_js/common_static/js/vendor/require.js
- js/RequireJS-namespace-undefine.js - js/RequireJS-namespace-undefine.js
- xmodule_js/common_static/js/vendor/requirejs/text.js
- xmodule_js/common_static/js/vendor/jquery.min.js - xmodule_js/common_static/js/vendor/jquery.min.js
- xmodule_js/common_static/js/vendor/jquery-ui.min.js - xmodule_js/common_static/js/vendor/jquery-ui.min.js
- xmodule_js/common_static/js/vendor/jquery.cookie.js - xmodule_js/common_static/js/vendor/jquery.cookie.js
...@@ -64,10 +65,12 @@ lib_paths: ...@@ -64,10 +65,12 @@ lib_paths:
src_paths: src_paths:
- js - js
- js/common_helpers - js/common_helpers
- teams/js
# Paths to spec (test) JavaScript files # Paths to spec (test) JavaScript files
spec_paths: spec_paths:
- js/spec - js/spec
- teams/js/spec
# Paths to fixture files (optional) # Paths to fixture files (optional)
# The fixture path will be set automatically when using jasmine-jquery. # The fixture path will be set automatically when using jasmine-jquery.
...@@ -91,6 +94,7 @@ fixture_paths: ...@@ -91,6 +94,7 @@ fixture_paths:
- js/fixtures/edxnotes - js/fixtures/edxnotes
- js/fixtures/search - js/fixtures/search
- templates/search - templates/search
- teams/templates
- templates/discovery - templates/discovery
requirejs: requirejs:
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
paths: { paths: {
"annotator_1.2.9": "js/vendor/edxnotes/annotator-full.min", "annotator_1.2.9": "js/vendor/edxnotes/annotator-full.min",
"date": "js/vendor/date", "date": "js/vendor/date",
"text": 'js/vendor/requirejs/text',
"backbone": "js/vendor/backbone-min", "backbone": "js/vendor/backbone-min",
"backbone-super": "js/vendor/backbone-super", "backbone-super": "js/vendor/backbone-super",
"underscore.string": "js/vendor/underscore.string.min", "underscore.string": "js/vendor/underscore.string.min",
...@@ -67,7 +68,7 @@ ...@@ -67,7 +68,7 @@
"osda": 'js/vendor/ova/OpenSeaDragonAnnotation', "osda": 'js/vendor/ova/OpenSeaDragonAnnotation',
"ova": 'js/vendor/ova/ova', "ova": 'js/vendor/ova/ova',
"catch": 'js/vendor/ova/catch/js/catch', "catch": 'js/vendor/ova/catch/js/catch',
"handlebars": 'js/vendor/ova/catch/js/handlebars-1.1.2', "handlebars": 'js/vendor/ova/catch/js/handlebars-1.1.2'
// end of files needed by OVA // end of files needed by OVA
}, },
shim: { shim: {
...@@ -89,7 +90,7 @@ ...@@ -89,7 +90,7 @@
exports: "Backbone" exports: "Backbone"
}, },
"backbone-super": { "backbone-super": {
deps: ["backbone"], deps: ["backbone"]
}, },
"logger": { "logger": {
exports: "Logger" exports: "Logger"
...@@ -147,7 +148,7 @@ ...@@ -147,7 +148,7 @@
"grouping-annotator", "diacritic-annotator", "openseadragon", "jquery-Watch", "catch", "handlebars", "grouping-annotator", "diacritic-annotator", "openseadragon", "jquery-Watch", "catch", "handlebars",
"URI" "URI"
] ]
}, }
// End of needed by OVA // End of needed by OVA
} }
}; };
......
../djangoapps/teams/static/teams
\ No newline at end of file
...@@ -428,6 +428,12 @@ if settings.COURSEWARE_ENABLED: ...@@ -428,6 +428,12 @@ if settings.COURSEWARE_ENABLED:
url(r'^api/branding/v1/', include('branding.api_urls')), url(r'^api/branding/v1/', include('branding.api_urls')),
) )
if settings.FEATURES["ENABLE_TEAMS"]:
# Teams endpoints
urlpatterns += (
url(r'^courses/{}/teams'.format(settings.COURSE_ID_PATTERN), include('teams.urls'), name="teams_endpoints"),
)
# allow course staff to change to student view of courseware # allow course staff to change to student view of courseware
if settings.FEATURES.get('ENABLE_MASQUERADE'): if settings.FEATURES.get('ENABLE_MASQUERADE'):
urlpatterns += ( urlpatterns += (
......
...@@ -33,6 +33,7 @@ setup( ...@@ -33,6 +33,7 @@ setup(
"progress = lms.djangoapps.courseware.tabs:ProgressCourseViewType", "progress = lms.djangoapps.courseware.tabs:ProgressCourseViewType",
"static_tab = lms.djangoapps.courseware.tabs:StaticCourseViewType", "static_tab = lms.djangoapps.courseware.tabs:StaticCourseViewType",
"syllabus = lms.djangoapps.courseware.tabs:SyllabusCourseViewType", "syllabus = lms.djangoapps.courseware.tabs:SyllabusCourseViewType",
"teams = lms.djangoapps.teams.plugins:TeamsCourseViewType",
"textbooks = lms.djangoapps.courseware.tabs:TextbookCourseViews", "textbooks = lms.djangoapps.courseware.tabs:TextbookCourseViews",
"wiki = lms.djangoapps.course_wiki.tab:WikiCourseViewType", "wiki = lms.djangoapps.course_wiki.tab:WikiCourseViewType",
......
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