Updates XBlock renders its own template. Adds expand and collapse JS + jasmine tests.

ECOM-2809
This commit is contained in:
Bill DeRusha
2015-11-17 15:16:27 -05:00
committed by Peter Fogg
parent 75bef91e17
commit e75f7950d4
12 changed files with 249 additions and 36 deletions

View File

@@ -160,25 +160,13 @@ def _get_index(passed_id=None):
return 0
def _get_html(course_updates_items):
"""
Method to create course_updates_html from course_updates items
"""
list_items = []
for update in reversed(course_updates_items):
# filter course update items which have status "deleted".
if update.get("status") != CourseInfoModule.STATUS_DELETED:
list_items.append(u"<article><h2>{date}</h2>{content}</article>".format(**update))
return u"<section>{list_items}</section>".format(list_items="".join(list_items))
def save_course_update_items(location, course_updates, course_update_items, user=None):
"""
Save list of course_updates data dictionaries in new field ("course_updates.items")
and html related to course update in 'data' ("course_updates.data") field.
"""
course_updates.items = course_update_items
course_updates.data = _get_html(course_update_items)
course_updates.data = ""
# update db record
modulestore().update_item(course_updates, user.id)

View File

@@ -173,9 +173,8 @@ class CourseUpdateTest(CourseTestCase):
self.assertHTMLEqual(update_content, json.loads(resp.content)['content'])
course_updates = modulestore().get_item(location)
self.assertEqual(course_updates.items, [{u'date': update_date, u'content': update_content, u'id': 1}])
# course_updates 'data' field should update accordingly
update_data = u"<section><article><h2>{date}</h2>{content}</article></section>".format(date=update_date, content=update_content)
self.assertEqual(course_updates.data, update_data)
# course_updates 'data' field should not update automatically
self.assertEqual(course_updates.data, '')
# test delete course update item (soft delete)
course_updates = modulestore().get_item(location)

View File

@@ -27,7 +27,7 @@ from openedx.core.lib.js_utils import escape_json_dumps
"${handouts_locator | escapejs}",
"${base_asset_url}",
${escape_json_dumps(push_notification_enabled) | n}
);
);
});
</%block>

View File

@@ -1,13 +1,14 @@
import os
import sys
import re
import copy
import logging
import textwrap
from lxml import etree
from path import Path as path
from datetime import datetime
from fs.errors import ResourceNotFoundError
import logging
from lxml import etree
import os
from path import Path as path
from pkg_resources import resource_string
import re
import sys
import textwrap
import dogstats_wrapper as dog_stats_api
from xmodule.util.misc import escape_html_characters
@@ -75,10 +76,10 @@ class HtmlBlock(object):
return Fragment(self.get_html())
def get_html(self):
"""
When we switch this to an XBlock, we can merge this with student_view,
but for now the XModule mixin requires that this method be defined.
"""
""" Returns html required for rendering XModule. """
# When we switch this to an XBlock, we can merge this with student_view,
# but for now the XModule mixin requires that this method be defined.
# pylint: disable=no-member
if self.system.anonymous_student_id:
return self.data.replace("%%USER_ID%%", self.system.anonymous_student_id)
@@ -417,6 +418,35 @@ class CourseInfoModule(CourseInfoFields, HtmlModuleMixin):
# statuses
STATUS_VISIBLE = 'visible'
STATUS_DELETED = 'deleted'
TEMPLATE_DIR = 'courseware'
@XBlock.supports("multi_device")
def student_view(self, _context):
"""
Return a fragment that contains the html for the student view
"""
return Fragment(self.get_html())
def get_html(self):
""" Returns html required for rendering XModule. """
# When we switch this to an XBlock, we can merge this with student_view,
# but for now the XModule mixin requires that this method be defined.
# pylint: disable=no-member
if self.data != "":
if self.system.anonymous_student_id:
return self.data.replace("%%USER_ID%%", self.system.anonymous_student_id)
return self.data
else:
course_updates = [item for item in self.items if item.get('status') == self.STATUS_VISIBLE]
course_updates.sort(key=lambda item: datetime.strptime(item['date'], '%B %d, %Y'), reverse=True)
context = {
'visible_updates': course_updates[:3],
'hidden_updates': course_updates[3:],
}
return self.system.render_template("{0}/course_updates.html".format(self.TEMPLATE_DIR), context)
@XBlock.tag("detached")

View File

@@ -0,0 +1,42 @@
;(function (define) {
'use strict';
define(["jquery"],
function ($) {
return function () {
// define variables for code legibility
var toggleActionElements = $('.toggle-visibility-button');
var updateToggleActionText = function (targetElement, actionElement) {
var show_text = actionElement.data('show');
var hide_text = actionElement.data('hide');
if (targetElement.is(":visible")) {
if (hide_text) {
actionElement.html(actionElement.data('hide'));
} else {
actionElement.hide();
}
} else {
if (show_text) {
actionElement.html(actionElement.data('show'));
}
}
};
$.each(toggleActionElements, function (i, elem) {
var toggleActionElement = $(elem);
var toggleTargetElement = toggleActionElement.siblings('.toggle-visibility-element');
updateToggleActionText(toggleTargetElement, toggleActionElement);
toggleActionElement.on('click', function (event) {
event.preventDefault();
toggleTargetElement.toggleClass('hidden');
updateToggleActionText(toggleTargetElement, toggleActionElement);
});
});
};
});
})(define || RequireJS.define);

View File

@@ -0,0 +1,45 @@
<div class="recent-updates">
<article>
<h2 class="date">December 1, 2015</h2>
<a class="toggle-visibility-button" data-hide="Hide" data-show="Show">Hide</a>
<div class="toggle-visibility-element article-content ">
<h1>Assignment 1</h1>
<p>Please submit your first assignment before due date.</p>
</div>
</article>
<article>
<h2 class="date">December 1, 2015</h2>
<a class="toggle-visibility-button" data-hide="Hide" data-show="Show">Show</a>
<div class="toggle-visibility-element article-content">
<h1>Quiz 1</h1>
<p>You have a quiz due on coming friday.</p>
</div>
</article>
<article>
<h2 class="date">November 26, 2015</h2>
<a class="toggle-visibility-button" data-hide="Hide" data-show="Show">Show</a>
<div class="toggle-visibility-element article-content hidden">
<h1>Assignment update</h1>
<p>Please submit your first assignment before due date.</p>
</div>
</article>
</div>
<div class="old-updates hidden toggle-visibility-element">
<article>
<h2 class="date">November 20, 2015</h2>
<a class="toggle-visibility-button" data-hide="Hide" data-show="Show">Show</a>
<div class="toggle-visibility-element article-content hidden"><h1>Orientation</h1>
<p>Orientation will held on monday</p>
</div>
</article>
<article>
<h2 class="date">November 19, 2015</h2>
<a class="toggle-visibility-button" data-hide="Hide" data-show="Show">Show</a>
<div class="toggle-visibility-element article-content hidden"><h1>Course starting</h1>
<p>Starting info about course.</p>
</div>
</article>
</div>
<a class="toggle-visibility-button show-older-updates" data-hide="" data-show="Show Earlier Course Updates">
Show Earlier Course Updates
</a>

View File

@@ -0,0 +1,36 @@
define(['jquery', 'js/courseware/toggle_element_visibility'],
function ($, ToggleElementVisibility) {
'use strict';
describe('show/hide with mouse click', function () {
beforeEach(function() {
loadFixtures('js/fixtures/courseware/course_updates.html');
/*jshint newcap: false */
ToggleElementVisibility();
/*jshint newcap: true */
});
it('ensures update will hide on hide button click', function () {
var $shownUpdate = $('.toggle-visibility-element:not(.hidden)').first();
$shownUpdate.siblings('.toggle-visibility-button').trigger('click');
expect($shownUpdate).toHaveClass('hidden');
});
it('ensures update will show on show button click', function () {
var $hiddenUpdate = $('.toggle-visibility-element.hidden').first();
$hiddenUpdate.siblings('.toggle-visibility-button').trigger('click');
expect($hiddenUpdate).not.toHaveClass('hidden');
});
it('ensures old updates will show on button click', function () {
// on page load old updates will be hidden
var $oldUpdates = $('.toggle-visibility-element.old-updates');
expect($oldUpdates).toHaveClass('hidden');
// on click on show earlier update button old updates will be shown
$('.toggle-visibility-button.show-older-updates').trigger('click');
expect($oldUpdates).not.toHaveClass('hidden');
});
});
});

View File

@@ -708,6 +708,7 @@
'lms/include/js/spec/edxnotes/collections/notes_spec.js',
'lms/include/js/spec/search/search_spec.js',
'lms/include/js/spec/navigation_spec.js',
'lms/include/js/spec/courseware/updates_visibility.js',
'lms/include/js/spec/discovery/collections/filters_spec.js',
'lms/include/js/spec/discovery/models/course_card_spec.js',
'lms/include/js/spec/discovery/models/course_directory_spec.js',

View File

@@ -50,6 +50,8 @@ div.info-wrapper {
@extend .content;
@include padding-left($baseline);
line-height: lh();
width: 100%;
display: block;
h1 {
@include text-align(left);
@@ -69,6 +71,25 @@ div.info-wrapper {
margin-bottom: lh();
padding-left: 0;
.updates-article {
border-radius:3px;
background-color: $white;
border:1px solid transparent;
&:hover {
border: 1px solid $gray-l3;
}
}
.show-older-updates {
@extend %btn-pl-white-base;
padding: ($baseline/2);
@include font-size(14);
width: 100%;
display: block;
text-align: center;
cursor: pointer;
}
> li,article {
@extend .clearfix;
padding: $baseline;
@@ -81,12 +102,25 @@ div.info-wrapper {
}
}
h2 {
h2.date {
@extend %t-title9;
margin-bottom: ($baseline/4);
text-transform: none;
background: url('#{$static-path}/images/calendar-icon.png') 0 center no-repeat;
@include padding-left($baseline);
@include float(left);
}
.toggle-visibility-button {
@extend %t-title9;
@include float(right);
cursor: pointer;
}
.toggle-visibility-element {
content:'';
display:block;
clear: both;
}
section.update-description {

View File

@@ -0,0 +1,29 @@
<%! from django.utils.translation import ugettext as _ %>
<section>
<div class="recent-updates">
% for index, update in enumerate(visible_updates):
<article class="updates-article">
% if not update.get("is_error"):
<h2 class="date">${update.get("date")}</h2>
<a class="toggle-visibility-button" data-hide="${_('Hide')}" data-show="${_('Show')}"></a>
% endif
<div class="toggle-visibility-element article-content ${'hidden' if index >= 1 else ''}">
${update.get("content")}
</div>
</article>
% endfor
</div>
<div class="old-updates hidden toggle-visibility-element">
% for update in hidden_updates:
<article class="updates-article">
<h2 class="date">${update.get("date")}</h2>
<a class="toggle-visibility-button" data-hide="${_('Hide')}" data-show="${_('Show')}"></a>
<div class="toggle-visibility-element article-content hidden">${update.get("content")}</div>
</article>
% endfor
</div>
% if len(hidden_updates) > 0:
<a class="toggle-visibility-button show-older-updates" data-hide="" data-show="${_('Show Earlier Course Updates')}"></a>
% endif
</section>

View File

@@ -34,6 +34,10 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
<%include file="/courseware/course_navigation.html" args="active_page='info'" />
<%static:require_module module_name="js/courseware/toggle_element_visibility" class_name="ToggleElementVisibility">
ToggleElementVisibility();
</%static:require_module>
<%block name="bodyclass">view-in-course view-course-info ${course.css_class or ''}</%block>
<section class="container">
<div class="home">

View File

@@ -92,13 +92,18 @@ def get_profile_image_urls_for_user(user, request=None):
dictionary of {size_display_name: url} for each image.
"""
if user.profile.has_profile_image:
urls = _get_profile_image_urls(
_make_profile_image_name(user.username),
get_profile_image_storage(),
version=user.profile.profile_image_uploaded_at.strftime("%s"),
)
else:
try:
if user.profile.has_profile_image:
urls = _get_profile_image_urls(
_make_profile_image_name(user.username),
get_profile_image_storage(),
version=user.profile.profile_image_uploaded_at.strftime("%s"),
)
else:
urls = _get_default_profile_image_urls()
except UserProfile.DoesNotExist:
# when user does not have profile it raises exception, when exception
# occur we can simply get default image.
urls = _get_default_profile_image_urls()
if request: