Commit 2eabe867 by Rocky Duan

Merge branch 'feature/bk_forum_int' of github.com:MITx/mitx into feature/bk_forum_int

parents 22657130 08e892ce
......@@ -263,7 +263,7 @@ def add_user_to_default_group(user, group):
utg.users.add(User.objects.get(username=user))
utg.save()
@receiver(post_save, sender=User)
# @receiver(post_save, sender=User)
def update_user_information(sender, instance, created, **kwargs):
try:
cc_user = cc.User.from_django_user(instance)
......@@ -274,7 +274,7 @@ def update_user_information(sender, instance, created, **kwargs):
log.error("update user info to discussion failed for user with id: " + str(instance.id))
########################## REPLICATION SIGNALS #################################
@receiver(post_save, sender=User)
# @receiver(post_save, sender=User)
def replicate_user_save(sender, **kwargs):
user_obj = kwargs['instance']
if not should_replicate(user_obj):
......@@ -282,7 +282,7 @@ def replicate_user_save(sender, **kwargs):
for course_db_name in db_names_to_replicate_to(user_obj.id):
replicate_user(user_obj, course_db_name)
@receiver(post_save, sender=CourseEnrollment)
# @receiver(post_save, sender=CourseEnrollment)
def replicate_enrollment_save(sender, **kwargs):
"""This is called when a Student enrolls in a course. It has to do the
following:
......@@ -308,12 +308,12 @@ def replicate_enrollment_save(sender, **kwargs):
user_profile = UserProfile.objects.get(user_id=enrollment_obj.user_id)
replicate_model(UserProfile.save, user_profile, enrollment_obj.user_id)
@receiver(post_delete, sender=CourseEnrollment)
# @receiver(post_delete, sender=CourseEnrollment)
def replicate_enrollment_delete(sender, **kwargs):
enrollment_obj = kwargs['instance']
return replicate_model(CourseEnrollment.delete, enrollment_obj, enrollment_obj.user_id)
@receiver(post_save, sender=UserProfile)
# @receiver(post_save, sender=UserProfile)
def replicate_userprofile_save(sender, **kwargs):
"""We just updated the UserProfile (say an update to the name), so push that
change to all Course DBs that we're enrolled in."""
......
......@@ -8,6 +8,7 @@ import logging
from datetime import datetime
from django.test import TestCase
from nose.plugins.skip import SkipTest
from .models import User, UserProfile, CourseEnrollment, replicate_user, USER_FIELDS_TO_COPY
......@@ -22,6 +23,7 @@ class ReplicationTest(TestCase):
def test_user_replication(self):
"""Test basic user replication."""
raise SkipTest()
portal_user = User.objects.create_user('rusty', 'rusty@edx.org', 'fakepass')
portal_user.first_name='Rusty'
portal_user.last_name='Skids'
......@@ -80,6 +82,7 @@ class ReplicationTest(TestCase):
def test_enrollment_for_existing_user_info(self):
"""Test the effect of Enrolling in a class if you've already got user
data to be copied over."""
raise SkipTest()
# Create our User
portal_user = User.objects.create_user('jack', 'jack@edx.org', 'fakepass')
portal_user.first_name = "Jack"
......@@ -143,6 +146,8 @@ class ReplicationTest(TestCase):
def test_enrollment_for_user_info_after_enrollment(self):
"""Test the effect of modifying User data after you've enrolled."""
raise SkipTest()
# Create our User
portal_user = User.objects.create_user('patty', 'patty@edx.org', 'fakepass')
portal_user.first_name = "Patty"
......
......@@ -80,8 +80,8 @@ def course_wiki_redirect(request, course_id):
urlpath = URLPath.create_article(
root,
course_slug,
title=course.title,
content="This is the wiki for " + course.title + ".",
title=course.number,
content="{0}\n===\nThis is the wiki for **{1}**'s _{2}_.".format(course.number, course.org, course.title),
user_message="Course page automatically created.",
user=None,
ip_address=None,
......
......@@ -155,8 +155,18 @@ def progress_summary(student, course, grader, student_module_cache):
chapters = []
# Don't include chapters that aren't displayable (e.g. due to error)
for c in course.get_display_items():
# Skip if the chapter is hidden
hidden = c.metadata.get('hide_from_toc','false')
if hidden.lower() == 'true':
continue
sections = []
for s in c.get_display_items():
# Skip if the section is hidden
hidden = s.metadata.get('hide_from_toc','false')
if hidden.lower() == 'true':
continue
# Same for sections
graded = s.metadata.get('graded', False)
scores = []
......
"""
This must be run only after seed_permissions_roles.py!
Creates default roles for all users currently in the database. Just runs through
Enrollments.
"""
from django.core.management.base import BaseCommand, CommandError
from student.models import CourseEnrollment
from django_comment_client.permissions import assign_default_role
class Command(BaseCommand):
args = 'course_id'
help = 'Seed default permisssions and roles'
def handle(self, *args, **options):
if len(args) != 0:
raise CommandError("This Command takes no arguments")
print "Updated roles for ",
for i, enrollment in enumerate(CourseEnrollment.objects.all(), start=1):
assign_default_role(None, enrollment)
if i % 1000 == 0:
print "{0}...".format(i),
print
\ No newline at end of file
......@@ -11,9 +11,9 @@ from util.cache import cache
@receiver(post_save, sender=CourseEnrollment)
def assign_default_role(sender, instance, **kwargs):
if instance.user.is_staff:
role = Role.objects.get(course_id=instance.course_id, name="Moderator")
role = Role.objects.get_or_create(course_id=instance.course_id, name="Moderator")[0]
else:
role = Role.objects.get(course_id=instance.course_id, name="Student")
role = Role.objects.get_or_create(course_id=instance.course_id, name="Student")[0]
logging.info("assign_default_role: adding %s as %s" % (instance.user, role))
instance.user.roles.add(role)
......
from django.contrib.auth.models import User
from django.utils import unittest
from django.test import TestCase
from student.models import CourseEnrollment, \
replicate_enrollment_save, \
replicate_enrollment_delete, \
......@@ -13,120 +13,11 @@ import random
from .permissions import has_permission
from .models import Role, Permission
# code adapted from https://github.com/justquick/django-activity-stream/issues/88
class NoSignalTestCase(unittest.TestCase):
def _receiver_in_lookup_keys(self, receiver, lookup_keys):
"""
Evaluate if the receiver is in the provided lookup_keys; instantly terminates when found.
"""
for key in lookup_keys:
if (receiver[0][0] == key[0] or key[0] is None) and receiver[0][1] == key[1]:
return True
return False
def _find_allowed_receivers(self, receivers, lookup_keys):
"""
Searches the receivers, keeping any that have a lookup_key in the lookup_keys list
"""
kept_receivers = []
for receiver in receivers:
if self._receiver_in_lookup_keys(receiver, lookup_keys):
kept_receivers.append(receiver)
return kept_receivers
def _create_lookup_keys(self, sender_receivers_tuple_list):
"""
Creates a signal lookup keys from the provided array of tuples.
"""
lookup_keys = []
for keep in sender_receivers_tuple_list:
receiver = keep[0]
sender = keep[1]
lookup_key = (_make_id(receiver) if receiver else receiver, _make_id(sender))
lookup_keys.append(lookup_key)
return lookup_keys
def _remove_disallowed_receivers(self, receivers, lookup_keys):
"""
Searches the receivers, discarding any that have a lookup_key in the lookup_keys list
"""
kept_receivers = []
for receiver in receivers:
if not self._receiver_in_lookup_keys(receiver, lookup_keys):
kept_receivers.append(receiver)
return kept_receivers
def setUp(self, sender_receivers_to_keep=None, sender_receivers_to_discard=None):
"""
Turns off signals from other apps
The `sender_receivers_to_keep` can be set to an array of tuples (reciever, sender,), preserving matching signals.
The `sender_receivers_to_discard` can be set to an array of tuples (reciever, sender,), discarding matching signals.
with both, you can set the `receiver` to None if you want to target all signals for a model
"""
self.m2m_changed_receivers = m2m_changed.receivers
self.pre_delete_receivers = pre_delete.receivers
self.pre_save_receivers = pre_save.receivers
self.post_delete_receivers = post_delete.receivers
self.post_save_receivers = post_save.receivers
new_m2m_changed_receivers = []
new_pre_delete_receivers = []
new_pre_save_receivers = []
new_post_delete_receivers = []
new_post_save_receivers = []
if sender_receivers_to_keep:
lookup_keys = self._create_lookup_keys(sender_receivers_to_keep)
new_m2m_changed_receivers = self._find_allowed_receivers(self.m2m_changed_receivers, lookup_keys)
new_pre_delete_receivers = self._find_allowed_receivers(self.pre_delete_receivers, lookup_keys)
new_pre_save_receivers = self._find_allowed_receivers(self.pre_save_receivers, lookup_keys)
new_post_delete_receivers = self._find_allowed_receivers(self.post_delete_receivers, lookup_keys)
new_post_save_receivers = self._find_allowed_receivers(self.post_save_receivers, lookup_keys)
if sender_receivers_to_discard:
lookup_keys = self._create_lookup_keys(sender_receivers_to_discard)
new_m2m_changed_receivers = self._remove_disallowed_receivers(new_m2m_changed_receivers or self.m2m_changed_receivers, lookup_keys)
new_pre_delete_receivers = self._remove_disallowed_receivers(new_pre_delete_receivers or self.pre_delete_receivers, lookup_keys)
new_pre_save_receivers = self._remove_disallowed_receivers(new_pre_save_receivers or self.pre_save_receivers, lookup_keys)
new_post_delete_receivers = self._remove_disallowed_receivers(new_post_delete_receivers or self.post_delete_receivers, lookup_keys)
new_post_save_receivers = self._remove_disallowed_receivers(new_post_save_receivers or self.post_save_receivers, lookup_keys)
m2m_changed.receivers = new_m2m_changed_receivers
pre_delete.receivers = new_pre_delete_receivers
pre_save.receivers = new_pre_save_receivers
post_delete.receivers = new_post_delete_receivers
post_save.receivers = new_post_save_receivers
super(NoSignalTestCase, self).setUp()
def tearDown(self):
"""
Restores the signals that were turned off.
"""
super(NoSignalTestCase, self).tearDown()
m2m_changed.receivers = self.m2m_changed_receivers
pre_delete.receivers = self.pre_delete_receivers
pre_save.receivers = self.pre_save_receivers
post_delete.receivers = self.post_delete_receivers
post_save.receivers = self.post_save_receivers
class PermissionsTestCase(NoSignalTestCase):
class PermissionsTestCase(TestCase):
def random_str(self, length=15, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for x in range(length))
def setUp(self):
sender_receivers_to_discard = [
(replicate_enrollment_save, CourseEnrollment),
(replicate_enrollment_delete, CourseEnrollment),
(update_user_information, User),
(replicate_user_save, User),
]
super(PermissionsTestCase, self).setUp(sender_receivers_to_discard=sender_receivers_to_discard)
self.course_id = "MITx/6.002x/2012_Fall"
self.moderator_role = Role.objects.get_or_create(name="Moderator", course_id=self.course_id)[0]
......@@ -144,9 +35,10 @@ class PermissionsTestCase(NoSignalTestCase):
def tearDown(self):
self.student_enrollment.delete()
self.moderator_enrollment.delete()
self.student.delete()
self.moderator.delete()
super(PermissionsTestCase, self).tearDown()
# Do we need to have this? We shouldn't be deleting students, ever
# self.student.delete()
# self.moderator.delete()
def testDefaultRoles(self):
self.assertTrue(self.student_role in self.student.roles.all())
......
......@@ -39,7 +39,7 @@ def update_template_dictionary(dictionary, request=None, course=None, article=No
if course:
dictionary['course'] = course
if 'namespace' not in dictionary:
dictionary['namespace'] = course.wiki_namespace
dictionary['namespace'] = "edX"
else:
dictionary['course'] = None
......@@ -99,7 +99,7 @@ def root_redirect(request, course_id=None):
course = get_opt_course_with_access(request.user, course_id, 'load')
#TODO: Add a default namespace to settings.
namespace = course.wiki_namespace if course else "edX"
namespace = "edX"
try:
root = Article.get_root(namespace)
......@@ -479,7 +479,7 @@ def not_found(request, article_path, course):
"""Generate a NOT FOUND message for some URL"""
d = {'wiki_err_notfound': True,
'article_path': article_path,
'namespace': course.wiki_namespace}
'namespace': "edX"}
update_template_dictionary(d, request, course)
return render_to_response('simplewiki/simplewiki_error.html', d)
......
......@@ -304,6 +304,7 @@ SIMPLE_WIKI_REQUIRE_LOGIN_VIEW = False
################################# WIKI ###################################
WIKI_ACCOUNT_HANDLING = False
WIKI_EDITOR = 'course_wiki.editors.CodeMirror'
WIKI_SHOW_MAX_CHILDREN = 0 # We don't use the little menu that shows children of an article in the breadcrumb
################################# Jasmine ###################################
JASMINE_TEST_DIRECTORY = PROJECT_ROOT + '/static/coffee'
......
......@@ -84,11 +84,17 @@ DATABASES = {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "course1.db",
},
'edx/full/6.002_Spring_2012': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "course2.db",
}
},
'edX/toy/TT_2012_Fall': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "course3.db",
},
}
CACHES = {
......
......@@ -21,7 +21,7 @@
<%static:js group='courseware'/>
<%include file="discussion/_js_dependencies.html" />
<%include file="../discussion/_js_dependencies.html" />
<%include file="/mathjax_include.html" />
<!-- TODO: http://docs.jquery.com/Plugins/Validation -->
......@@ -59,8 +59,8 @@
<div id="course-errors">
<ul>
% for (msg, err) in course_errors:
<li>${msg}
<ul><li><pre>${err}</pre></li></ul>
<li>${msg | h}
<ul><li><pre>${err | h}</pre></li></ul>
</li>
% endfor
</ul>
......
......@@ -69,6 +69,7 @@ ${"Edit " + wiki_title + " - " if wiki_title is not UNDEFINED else ""}MITx 6.002
%else:
<input type="submit" id="submit_edit" name="edit" value="Save Changes" />
<input type="submit" id="submit_delete" name="delete" value="Delete article" />
%endif
<%include file="simplewiki_instructions.html"/>
......
......@@ -52,6 +52,9 @@
</style>
{% endaddtoblock %}
<p class="lead">
{% trans "Click each revision to see a list of edited lines. Click the Preview button to see how the article looked at this stage. At the bottom of this page, you can change to a particular revision or merge an old revision with the current one." %}
</p>
<form method="GET">
<div class="tab-content" style="overflow: visible;">
......@@ -60,20 +63,14 @@
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" style="float: left;" href="#collapse{{ revision.revision_number }}" onclick="get_diff_json('{% url 'wiki:diff' revision.id %}', $('#collapse{{ revision.revision_number }}'))">
{{ revision.created }} (#{{ revision.revision_number }}) by {% if revision.user %}{{ revision.user }}{% else %}{% if user|is_moderator %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
{% if revision == article.current_revision %}
<strong>*</strong>
{% endif %}
{% if revision.deleted %}
<span class="badge badge-important">{% trans "deleted" %}</span>
{% endif %}
{% if revision.previous_revision.deleted and not revision.deleted %}
<span class="badge badge-success">{% trans "restored" %}</span>
{% endif %}
<span class="icon-plus"></span>
{% include "wiki/includes/revision_info.html" with current_revision=article.current_revision %}
<div style="color: #CCC;">
<small>
{% if revision.user_message %}
{{ revision.user_message }}
{% elif revision.automatic_log %}
{{ revision.automatic_log }}
{% else %}
({% trans "no log message" %})
{% endif %}
......@@ -84,21 +81,12 @@
<div class="bar" style="width: 100%;"></div>
</div>
<div class="pull-right" style="vertical-align: middle; margin: 8px 3px;">
{% if revision == article.current_revision %}
<a href="#" class="btn disabled">
<span class="icon-lock"></span>
{% trans "Preview this version" %}
</a>
{% else %}
{% if not revision == article.current_revision %}
<button type="submit" class="btn" onclick="$('#previewModal').modal('show'); this.form.target='previewWindow'; this.form.r.value='{{ revision.id }}'; this.form.action='{% url 'wiki:preview_revision' article.id %}'; $('#previewModal .switch-to-revision').attr('href', '{% url 'wiki:change_revision' path=urlpath.path article_id=article.id revision_id=revision.id %}')">
<span class="icon-eye-open"></span>
{% trans "Preview this version" %}
{% trans "Preview this revision" %}
</button>
{% endif %}
<a class="btn btn-info" href="#collapse{{ revision.revision_number }}" onclick="get_diff_json('{% url 'wiki:diff' revision_id=revision.id %}', $('#collapse{{ revision.revision_number }}'))">
<span class="icon-list-alt"></span>
{% trans "Show changes" %}
</a>
{% if article|can_write:user %}
<input type="radio"{% if revision == article.current_revision %} disabled="true"{% endif %} style="margin: 0 10px;" value="{{ revision.id }}" name="revision_id" switch-button-href="{% url 'wiki:change_revision' path=urlpath.path revision_id=revision.id %}" merge-button-href="{% url 'wiki:merge_revision_preview' article_id=article.id revision_id=revision.id %}" merge-button-commit-href="{% url 'wiki:merge_revision' path=urlpath.path article_id=article.id revision_id=revision.id %}" />
......@@ -157,7 +145,7 @@
<input type="hidden" name="r" value="" />
<div class="modal hide fade" id="previewModal">
<div class="modal-body">
<iframe name="previewWindow" frameborder="0"></iframe>
<iframe name="previewWindow"></iframe>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-large" data-dismiss="modal">
......@@ -184,7 +172,7 @@
<p class="lead"><span class="icon-info-sign"></span> {% trans "When you merge a revision with the current, all data will be retained from both versions and merged at its approximate location from each revision." %} <strong>{% trans "After this, it's important to do a manual review." %}</strong></p>
</div>
<div class="modal-body">
<iframe name="mergeWindow" frameborder="0"></iframe>
<iframe name="mergeWindow"></iframe>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-large" data-dismiss="modal">
......
{% load i18n %}{% load url from future %}
{% if urlpath %}
## mako
<%! from django.core.urlresolvers import reverse %>
%if urlpath is not Undefined and urlpath:
<header>
<ul class="breadcrumb pull-left" class="">
{% for ancestor in urlpath.get_ancestors.all %}
<li><a href="{% url 'wiki:get' path=ancestor.path %}">{{ ancestor.article.current_revision.title }}</a></li>
{% endfor %}
<li class="active"><a href="{% url 'wiki:get' path=urlpath.path %}">{{ article.current_revision.title }}</a></li>
<%
# The create button links to the highest ancestor we have edit priveleges to
create_article_root = None
%>
%for ancestor in urlpath.cached_ancestors:
<li><a href="${reverse('wiki:get', kwargs={'path' : ancestor.path})}">${ancestor.article.current_revision.title}</a></li>
<%
if not create_article_root and ancestor.article.can_write(user):
create_article_root = ancestor
%>
%endfor
<li class="active"><a href="${reverse('wiki:get', kwargs={'path' : urlpath.path})}">${article.current_revision.title}</a></li>
<%
if not create_article_root and urlpath.article.can_write(user):
create_article_root = urlpath
%>
</ul>
<div class="pull-left" style="margin-left: 10px;">
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#" style="padding: 7px;" title="{% trans "Sub-articles for" %} {{ article.current_revision.title }}">
<span class="icon-list"></span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
{% for child in children_slice %}
<li>
<a href="{% url 'wiki:get' path=child.path %}">
{{ child.article.current_revision.title }}
</a>
</li>
{% empty %}
<li><a href="#"><em>{% trans "No sub-articles" %}</em></a></li>
{% endfor %}
{% if children_slice_more %}
<li><a href="#"><em>{% trans "...and more" %}</em></a></li>
{% endif %}
<li class="divider"></li>
<li>
<a href="" onclick="alert('TODO')">{% trans "List sub-pages" %} &raquo;</a>
</li>
</ul>
</div>
</div>
<div class="global-functions pull-right">
<form class="search-wiki pull-left">
<!-- <form class="search-wiki pull-left">
<input type="search" placeholder="search wiki" />
</form>
<a class="add-article-btn btn pull-left" href="{% url 'wiki:create' path=urlpath.path %}" style="padding: 7px;">
</form> -->
%if create_article_root:
<a class="add-article-btn btn pull-left" href="${reverse('wiki:create', kwargs={'path' : create_article_root.path})}" style="padding: 7px;">
<span class="icon-plus"></span>
{% trans "Add article" %}
Add article
</a>
%endif
</div>
</header>
{% endif %}
%endif
{% extends "wiki/base.html" %}
{% load wiki_tags i18n %}
{% load url from future %}
{% block pagetitle %}{{ article.current_revision.title }}{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
<div class="missing-wrapper">
<p>This article was not found, and neither was the parent. <a href="#">Go back to the main wiki article.</a></p>
<button type="submit">Create a new article</button>
</div>
{% endblock %}
-e git://github.com/MITx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles
-e git://github.com/MITx/django-pipeline.git#egg=django-pipeline
-e git://github.com/benjaoming/django-wiki.git@c145596#egg=django-wiki
-e git://github.com/dementrock/pystache_custom.git#egg=pystache_custom
-e git://github.com/benjaoming/django-wiki.git@e237b2ac#egg=django-wiki
-e common/lib/capa
-e common/lib/xmodule
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