From 73477ec1bf1177ea1ad84a636404a80afefa03fa Mon Sep 17 00:00:00 2001 From: ichuang Date: Fri, 12 Oct 2012 21:06:15 -0400 Subject: [PATCH 01/40] reset_problem in capa_module should always return dicts not strings --- common/lib/xmodule/xmodule/capa_module.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index f6054c494d..4e6a27c14e 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -618,12 +618,14 @@ class CapaModule(XModule): if self.closed(): event_info['failure'] = 'closed' self.system.track_function('reset_problem_fail', event_info) - return "Problem is closed" + return {'success': False, + 'error': "Problem is closed"} if not self.lcp.done: event_info['failure'] = 'not_done' self.system.track_function('reset_problem_fail', event_info) - return "Refresh the page and make an attempt before resetting." + return {'success': False, + 'error': "Refresh the page and make an attempt before resetting."} self.lcp.do_reset() if self.rerandomize in ["always", "onreset"]: From f846f4c2adaa0ce013ed7dcacba7feed60b64cc1 Mon Sep 17 00:00:00 2001 From: ichuang Date: Sat, 13 Oct 2012 22:33:41 -0400 Subject: [PATCH 02/40] add import filter capability to html & problem editing; provides latex2edx interface --- cms/envs/dev_ike.py | 8 + cms/templates/widgets/metadata-edit.html | 18 +- cms/templates/widgets/source-edit.html | 109 +++++++++++ .../xmodule/templates/html/latex_html.yaml | 23 +++ .../templates/problem/latex_problem.yaml | 27 +++ common/static/js/vendor/CodeMirror/stex.js | 182 ++++++++++++++++++ 6 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 cms/envs/dev_ike.py create mode 100644 cms/templates/widgets/source-edit.html create mode 100644 common/lib/xmodule/xmodule/templates/html/latex_html.yaml create mode 100644 common/lib/xmodule/xmodule/templates/problem/latex_problem.yaml create mode 100644 common/static/js/vendor/CodeMirror/stex.js diff --git a/cms/envs/dev_ike.py b/cms/envs/dev_ike.py new file mode 100644 index 0000000000..ec769ff6ab --- /dev/null +++ b/cms/envs/dev_ike.py @@ -0,0 +1,8 @@ +# dev environment for ichuang/mit + +from .common import * +from logsettings import get_logger_config +from .dev import * +import socket + +#MITX_FEATURES['USE_DJANGO_PIPELINE']=False # don't recompile scss diff --git a/cms/templates/widgets/metadata-edit.html b/cms/templates/widgets/metadata-edit.html index f960ecfebd..069db424da 100644 --- a/cms/templates/widgets/metadata-edit.html +++ b/cms/templates/widgets/metadata-edit.html @@ -1,10 +1,26 @@ % if metadata: +<% + import hashlib + hlskey = hashlib.md5(module.location.url()).hexdigest() +%> % endif diff --git a/cms/templates/widgets/source-edit.html b/cms/templates/widgets/source-edit.html new file mode 100644 index 0000000000..42b85ded8b --- /dev/null +++ b/cms/templates/widgets/source-edit.html @@ -0,0 +1,109 @@ +<% + import hashlib + hlskey = hashlib.md5(module.location.url()).hexdigest() +%> + + + + + diff --git a/common/lib/xmodule/xmodule/templates/html/latex_html.yaml b/common/lib/xmodule/xmodule/templates/html/latex_html.yaml new file mode 100644 index 0000000000..439565c16e --- /dev/null +++ b/common/lib/xmodule/xmodule/templates/html/latex_html.yaml @@ -0,0 +1,23 @@ +--- +metadata: + display_name: E-text Written in LaTeX + source_processor_url: https://qisx.mit.edu:5443/latex2edx + source_code: | + \subsection{Example of E-text in LaTeX} + + It is very convenient to write complex equations in LaTeX. + + \begin{equation} + x = \frac{-b\pm\sqrt{b^2-4*a*c}}{2a} + \end{equation} + + Seize the moment. + +data: | + +

Example: E-text page

+

+ It is very convenient to write complex equations in LaTeX. +

+ +children: [] diff --git a/common/lib/xmodule/xmodule/templates/problem/latex_problem.yaml b/common/lib/xmodule/xmodule/templates/problem/latex_problem.yaml new file mode 100644 index 0000000000..fc9cae254d --- /dev/null +++ b/common/lib/xmodule/xmodule/templates/problem/latex_problem.yaml @@ -0,0 +1,27 @@ +--- +metadata: + display_name: Problem Written in LaTeX + source_processor_url: https://qisx.mit.edu:5443/latex2edx + source_code: | + \subsection{Example: Option Response Problem in Latex} + + Where is the earth? + + \edXabox{options='up','down' expect='down'} + +data: | + + +

Example: Option Response Problem

+

+ An option response problem presents option boxes for students to select from. Correctness of input is evaluated based + on which option is defined as correct in the optioninput statement. +

+

Select the correct options:

+ +

The location of the sky is:

+

The location of the earth is:

+
+
+
+children: [] diff --git a/common/static/js/vendor/CodeMirror/stex.js b/common/static/js/vendor/CodeMirror/stex.js new file mode 100644 index 0000000000..7d9495f9f7 --- /dev/null +++ b/common/static/js/vendor/CodeMirror/stex.js @@ -0,0 +1,182 @@ +/* + * Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de) + * Licence: MIT + */ + +CodeMirror.defineMode("stex", function(cmCfg, modeCfg) +{ + function pushCommand(state, command) { + state.cmdState.push(command); + } + + function peekCommand(state) { + if (state.cmdState.length>0) + return state.cmdState[state.cmdState.length-1]; + else + return null; + } + + function popCommand(state) { + if (state.cmdState.length>0) { + var plug = state.cmdState.pop(); + plug.closeBracket(); + } + } + + function applyMostPowerful(state) { + var context = state.cmdState; + for (var i = context.length - 1; i >= 0; i--) { + var plug = context[i]; + if (plug.name=="DEFAULT") + continue; + return plug.styleIdentifier(); + } + return null; + } + + function addPluginPattern(pluginName, cmdStyle, brackets, styles) { + return function () { + this.name=pluginName; + this.bracketNo = 0; + this.style=cmdStyle; + this.styles = styles; + this.brackets = brackets; + + this.styleIdentifier = function(content) { + if (this.bracketNo<=this.styles.length) + return this.styles[this.bracketNo-1]; + else + return null; + }; + this.openBracket = function(content) { + this.bracketNo++; + return "bracket"; + }; + this.closeBracket = function(content) { + }; + }; + } + + var plugins = new Array(); + + plugins["importmodule"] = addPluginPattern("importmodule", "tag", "{[", ["string", "builtin"]); + plugins["documentclass"] = addPluginPattern("documentclass", "tag", "{[", ["", "atom"]); + plugins["usepackage"] = addPluginPattern("documentclass", "tag", "[", ["atom"]); + plugins["begin"] = addPluginPattern("documentclass", "tag", "[", ["atom"]); + plugins["end"] = addPluginPattern("documentclass", "tag", "[", ["atom"]); + + plugins["DEFAULT"] = function () { + this.name="DEFAULT"; + this.style="tag"; + + this.styleIdentifier = function(content) { + }; + this.openBracket = function(content) { + }; + this.closeBracket = function(content) { + }; + }; + + function setState(state, f) { + state.f = f; + } + + function normal(source, state) { + if (source.match(/^\\[a-zA-Z@]+/)) { + var cmdName = source.current(); + cmdName = cmdName.substr(1, cmdName.length-1); + var plug; + if (plugins.hasOwnProperty(cmdName)) { + plug = plugins[cmdName]; + } else { + plug = plugins["DEFAULT"]; + } + plug = new plug(); + pushCommand(state, plug); + setState(state, beginParams); + return plug.style; + } + + // escape characters + if (source.match(/^\\[$&%#{}_]/)) { + return "tag"; + } + + // white space control characters + if (source.match(/^\\[,;!\/]/)) { + return "tag"; + } + + var ch = source.next(); + if (ch == "%") { + // special case: % at end of its own line; stay in same state + if (!source.eol()) { + setState(state, inCComment); + } + return "comment"; + } + else if (ch=='}' || ch==']') { + plug = peekCommand(state); + if (plug) { + plug.closeBracket(ch); + setState(state, beginParams); + } else + return "error"; + return "bracket"; + } else if (ch=='{' || ch=='[') { + plug = plugins["DEFAULT"]; + plug = new plug(); + pushCommand(state, plug); + return "bracket"; + } + else if (/\d/.test(ch)) { + source.eatWhile(/[\w.%]/); + return "atom"; + } + else { + source.eatWhile(/[\w-_]/); + return applyMostPowerful(state); + } + } + + function inCComment(source, state) { + source.skipToEnd(); + setState(state, normal); + return "comment"; + } + + function beginParams(source, state) { + var ch = source.peek(); + if (ch == '{' || ch == '[') { + var lastPlug = peekCommand(state); + var style = lastPlug.openBracket(ch); + source.eat(ch); + setState(state, normal); + return "bracket"; + } + if (/[ \t\r]/.test(ch)) { + source.eat(ch); + return null; + } + setState(state, normal); + lastPlug = peekCommand(state); + if (lastPlug) { + popCommand(state); + } + return normal(source, state); + } + + return { + startState: function() { return { f:normal, cmdState:[] }; }, + copyState: function(s) { return { f: s.f, cmdState: s.cmdState.slice(0, s.cmdState.length) }; }, + + token: function(stream, state) { + var t = state.f(stream, state); + var w = stream.current(); + return t; + } + }; +}); + +CodeMirror.defineMIME("text/x-stex", "stex"); +CodeMirror.defineMIME("text/x-latex", "stex"); From db8f69d497a897039c331c099c7fe6bc6dcb25fa Mon Sep 17 00:00:00 2001 From: ichuang Date: Sat, 13 Oct 2012 23:10:51 -0400 Subject: [PATCH 03/40] fix tabs in latex_html.yaml template --- .../lib/xmodule/xmodule/templates/html/latex_html.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/lib/xmodule/xmodule/templates/html/latex_html.yaml b/common/lib/xmodule/xmodule/templates/html/latex_html.yaml index 439565c16e..e3962d60a6 100644 --- a/common/lib/xmodule/xmodule/templates/html/latex_html.yaml +++ b/common/lib/xmodule/xmodule/templates/html/latex_html.yaml @@ -5,13 +5,13 @@ metadata: source_code: | \subsection{Example of E-text in LaTeX} - It is very convenient to write complex equations in LaTeX. + It is very convenient to write complex equations in LaTeX. - \begin{equation} - x = \frac{-b\pm\sqrt{b^2-4*a*c}}{2a} - \end{equation} + \begin{equation} + x = \frac{-b\pm\sqrt{b^2-4*a*c}}{2a} + \end{equation} - Seize the moment. + Seize the moment. data: | From 09d7d6d6881f97eb85154ce111337f34d6f9f5ea Mon Sep 17 00:00:00 2001 From: ichuang Date: Sun, 14 Oct 2012 16:40:04 -0400 Subject: [PATCH 04/40] add AUTH_USE_MIT_CERTIFICATES feature flag to CMS, and @ssl_login_shortcut --- cms/djangoapps/contentstore/views.py | 3 +- cms/envs/common.py | 3 +- cms/envs/dev_ike.py | 15 +++++- common/djangoapps/external_auth/views.py | 58 +++++++++++++++++++----- 4 files changed, 65 insertions(+), 14 deletions(-) diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 93f867549c..dd85c207e6 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -37,6 +37,7 @@ from xmodule.error_module import ErrorDescriptor from xmodule.errortracker import exc_info_to_str from github_sync import export_to_github from static_replace import replace_urls +from external_auth.views import ssl_login_shortcut from mitxmako.shortcuts import render_to_response, render_to_string from xmodule.modulestore.django import modulestore @@ -88,7 +89,7 @@ def signup(request): csrf_token = csrf(request)['csrf_token'] return render_to_response('signup.html', {'csrf': csrf_token}) - +@ssl_login_shortcut @ensure_csrf_cookie def login_page(request): """ diff --git a/cms/envs/common.py b/cms/envs/common.py index 6f7a462da2..8b5e6c7655 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -32,7 +32,8 @@ from xmodule.static_content import write_descriptor_styles, write_descriptor_js, MITX_FEATURES = { 'USE_DJANGO_PIPELINE': True, 'GITHUB_PUSH': False, - 'ENABLE_DISCUSSION_SERVICE': False + 'ENABLE_DISCUSSION_SERVICE': False, + 'AUTH_USE_MIT_CERTIFICATES' : False, } # needed to use lms student app diff --git a/cms/envs/dev_ike.py b/cms/envs/dev_ike.py index ec769ff6ab..0fdca9113d 100644 --- a/cms/envs/dev_ike.py +++ b/cms/envs/dev_ike.py @@ -1,8 +1,21 @@ # dev environment for ichuang/mit +# FORCE_SCRIPT_NAME = '/cms' + from .common import * from logsettings import get_logger_config from .dev import * import socket -#MITX_FEATURES['USE_DJANGO_PIPELINE']=False # don't recompile scss +MITX_FEATURES['AUTH_USE_MIT_CERTIFICATES'] = True + +MITX_FEATURES['USE_DJANGO_PIPELINE']=False # don't recompile scss + +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') # django 1.4 for nginx ssl proxy + +MITX_ROOT_URL = 'https://qisx.mit.edu:442' +#MITX_ROOT_URL = 'cms' + +LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/login' +LOGIN_URL = MITX_ROOT_URL + '/login' + diff --git a/common/djangoapps/external_auth/views.py b/common/djangoapps/external_auth/views.py index 5cf21ca68d..520cd95c0b 100644 --- a/common/djangoapps/external_auth/views.py +++ b/common/djangoapps/external_auth/views.py @@ -215,6 +215,52 @@ def ssl_dn_extract_info(dn): else: return None return (user, email, fullname) + + +def ssl_get_cert_from_request(request): + """ + Extract user information from certificate, if it exists, returning (user, email, fullname). + Else return None. + """ + certkey = "SSL_CLIENT_S_DN" # specify the request.META field to use + + cert = request.META.get(certkey, '') + if not cert: + cert = request.META.get('HTTP_' + certkey, '') + if not cert: + try: + # try the direct apache2 SSL key + cert = request._req.subprocess_env.get(certkey, '') + except Exception: + return '' + + return cert + + (user, email, fullname) = ssl_dn_extract_info(cert) + return (user, email, fullname) + + +def ssl_login_shortcut(fn): + """ + Python function decorator for login procedures, to allow direct login + based on existing ExternalAuth record and MIT ssl certificate. + """ + def wrapped(*args, **kwargs): + if not settings.MITX_FEATURES['AUTH_USE_MIT_CERTIFICATES']: + return fn(*args, **kwargs) + request = args[0] + cert = ssl_get_cert_from_request(request) + if not cert: # no certificate information - show normal login window + return fn(*args, **kwargs) + + (user, email, fullname) = ssl_dn_extract_info(cert) + return external_login_or_signup(request, + external_id=email, + external_domain="ssl:MIT", + credentials=cert, + email=email, + fullname=fullname) + return wrapped @csrf_exempt @@ -234,17 +280,7 @@ def ssl_login(request): Else continues on with student.views.index, and no authentication. """ - certkey = "SSL_CLIENT_S_DN" # specify the request.META field to use - - cert = request.META.get(certkey, '') - if not cert: - cert = request.META.get('HTTP_' + certkey, '') - if not cert: - try: - # try the direct apache2 SSL key - cert = request._req.subprocess_env.get(certkey, '') - except Exception: - cert = None + cert = ssl_get_cert_from_request(request) if not cert: # no certificate information - go onward to main index From 69bfdac49f8a2b6f39d53885039bfea68866a726 Mon Sep 17 00:00:00 2001 From: ichuang Date: Sun, 14 Oct 2012 16:58:11 -0400 Subject: [PATCH 05/40] minor change in dev_ike for cms-master --- cms/envs/dev_ike.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cms/envs/dev_ike.py b/cms/envs/dev_ike.py index 0fdca9113d..5fb120854b 100644 --- a/cms/envs/dev_ike.py +++ b/cms/envs/dev_ike.py @@ -13,9 +13,4 @@ MITX_FEATURES['USE_DJANGO_PIPELINE']=False # don't recompile scss SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') # django 1.4 for nginx ssl proxy -MITX_ROOT_URL = 'https://qisx.mit.edu:442' -#MITX_ROOT_URL = 'cms' - -LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/login' -LOGIN_URL = MITX_ROOT_URL + '/login' From 12869ecedd67c145616997dc1403bea55f908eb2 Mon Sep 17 00:00:00 2001 From: ichuang Date: Sun, 14 Oct 2012 17:48:45 -0400 Subject: [PATCH 06/40] fix hls modal window style (it now autosizes properly) --- cms/templates/widgets/source-edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/templates/widgets/source-edit.html b/cms/templates/widgets/source-edit.html index 42b85ded8b..e5fc5a2969 100644 --- a/cms/templates/widgets/source-edit.html +++ b/cms/templates/widgets/source-edit.html @@ -3,7 +3,7 @@ hlskey = hashlib.md5(module.location.url()).hexdigest() %> -