Commit e75f7950 by Bill DeRusha Committed by Peter Fogg

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

ECOM-2809
parent 75bef91e
......@@ -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)
......
......@@ -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)
......
......@@ -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>
......
import os
import sys
import re
import copy
from datetime import datetime
from fs.errors import ResourceNotFoundError
import logging
import textwrap
from lxml import etree
import os
from path import Path as path
from fs.errors import ResourceNotFoundError
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")
......
;(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);
<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>
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');
});
});
});
......@@ -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',
......
......@@ -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 {
......
<%! 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>
......@@ -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">
......
......@@ -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:
......
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