Commit 6c540351 by Andy Armstrong Committed by Brian Jacobel

Add a content header to the discussion board

parent 89f93df4
/* global $$course_id, Content, Discussion, DiscussionRouter, DiscussionCourseSettings,
DiscussionUser, DiscussionUserProfileView, DiscussionUtil */
(function() {
'use strict';
var DiscussionApp, DiscussionProfileApp;
if (typeof Backbone !== 'undefined' && Backbone !== null) {
DiscussionApp = {
start: function(elem) {
var content_info, course_settings, discussion, element, sort_preference, thread_pages, threads,
user, user_info;
DiscussionUtil.loadRolesFromContainer();
element = $(elem);
window.$$course_id = element.data('course-id');
window.courseName = element.data('course-name');
user_info = element.data('user-info');
sort_preference = element.data('sort-preference');
threads = element.data('threads');
thread_pages = element.data('thread-pages');
content_info = element.data('content-info');
user = new DiscussionUser(user_info);
DiscussionUtil.setUser(user);
window.user = user;
Content.loadContentInfos(content_info);
discussion = new Discussion(threads, {
pages: thread_pages,
sort: sort_preference
});
course_settings = new DiscussionCourseSettings(element.data('course-settings'));
new DiscussionRouter({ // eslint-disable-line no-new
discussion: discussion,
course_settings: course_settings
});
if (!Backbone.History.started) {
Backbone.history.start({pushState: true, root: '/courses/' + $$course_id + '/discussion/forum/'});
} else {
Backbone.history.loadUrl(window.location.pathname);
}
}
};
DiscussionProfileApp = {
start: function(elem) {
var element, numPages, page, threads, user_info;
DiscussionUtil.loadRoles({
'Moderator': [],
'Administrator': [],
'Community TA': []
});
element = $(elem);
window.$$course_id = element.data('course-id');
threads = element.data('threads');
user_info = element.data('user-info');
window.user = new DiscussionUser(user_info);
page = element.data('page');
numPages = element.data('num-pages');
return new DiscussionUserProfileView({
el: element,
collection: threads,
page: page,
numPages: numPages
});
}
};
$(function() {
$('section.discussion').each(function(index, elem) {
return DiscussionApp.start(elem);
});
return $('section.discussion-user-threads').each(function(index, elem) {
return DiscussionProfileApp.start(elem);
});
});
}
}).call(window);
......@@ -23,10 +23,6 @@
this.roleIds = roles;
};
DiscussionUtil.loadRolesFromContainer = function() {
return this.loadRoles($('#discussion-container').data('roles'));
};
DiscussionUtil.isStaff = function(userId) {
var staff;
if (_.isUndefined(userId)) {
......
......@@ -6,18 +6,26 @@
}
DiscussionSpecHelper.setUpGlobals = function() {
DiscussionUtil.loadRoles({
'Moderator': [],
'Administrator': [],
'Community TA': []
});
DiscussionUtil.loadRoles(DiscussionSpecHelper.getTestRoleInfo());
window.$$course_id = 'edX/999/test';
window.user = new DiscussionUser({
window.user = new DiscussionUser(DiscussionSpecHelper.getTestUserInfo());
return DiscussionUtil.setUser(window.user);
};
DiscussionSpecHelper.getTestUserInfo = function() {
return {
username: 'test_user',
id: '567',
upvoted_ids: []
});
return DiscussionUtil.setUser(window.user);
};
};
DiscussionSpecHelper.getTestRoleInfo = function() {
return {
'Moderator': [],
'Administrator': [],
'Community TA': []
};
};
DiscussionSpecHelper.makeTA = function() {
......@@ -42,69 +50,75 @@
return jasmine.createSpyObj('event', ['preventDefault', 'target']);
};
DiscussionSpecHelper.makeCourseSettings = function(is_cohorted) {
if (typeof is_cohorted === 'undefined' || is_cohorted === null) {
is_cohorted = true;
DiscussionSpecHelper.makeCourseSettings = function(isCohorted) {
if (typeof isCohorted === 'undefined' || isCohorted === null) {
isCohorted = true;
}
return new DiscussionCourseSettings({
category_map: {
children: ['Test Topic', 'Other Topic'],
entries: {
'Test Topic': {
is_cohorted: is_cohorted,
is_cohorted: isCohorted,
id: 'test_topic'
},
'Other Topic': {
is_cohorted: is_cohorted,
is_cohorted: isCohorted,
id: 'other_topic'
}
}
},
is_cohorted: is_cohorted
is_cohorted: isCohorted
});
};
DiscussionSpecHelper.setUnderscoreFixtures = function() {
var templateFixture, templateName, templateNames, templateNamesNoTrailingTemplate, _i, _j, _len, _len1;
var templateFixture, templateName, templateNames, templateNamesNoTrailingTemplate, i, j, len;
templateNames = [
'thread', 'thread-show', 'thread-edit', 'thread-response', 'thread-response-show',
'thread-response-edit', 'response-comment-show', 'response-comment-edit', 'thread-list-item',
'discussion-home', 'search-alert', 'new-post', 'thread-type', 'new-post-menu-entry',
'new-post-menu-category', 'topic', 'post-user-display', 'inline-discussion', 'pagination',
'user-profile', 'profile-thread', 'customwmd-prompt', 'nav-loading'
'profile-thread', 'customwmd-prompt', 'nav-loading'
];
templateNamesNoTrailingTemplate = [
'forum-action-endorse', 'forum-action-answer', 'forum-action-follow', 'forum-action-vote',
'forum-action-report', 'forum-action-pin', 'forum-action-close', 'forum-action-edit',
'forum-action-delete', 'forum-actions', 'alert-popup', 'nav-load-more-link'
];
for (_i = 0, _len = templateNames.length; _i < _len; _i++) {
templateName = templateNames[_i];
for (i = 0, len = templateNames.length; i < len; i++) {
templateName = templateNames[i];
templateFixture = readFixtures('common/templates/discussion/' + templateName + '.underscore');
appendSetFixtures($('<script>', {
id: templateName + '-template',
type: 'text/template'
}).text(templateFixture));
}
for (_j = 0, _len1 = templateNamesNoTrailingTemplate.length; _j < _len1; _j++) {
templateName = templateNamesNoTrailingTemplate[_j];
for (j = 0, len = templateNamesNoTrailingTemplate.length; j < len; j++) {
templateName = templateNamesNoTrailingTemplate[j];
templateFixture = readFixtures('common/templates/discussion/' + templateName + '.underscore');
appendSetFixtures($('<script>', {
id: templateName,
type: 'text/template'
}).text(templateFixture));
}
return appendSetFixtures(
'<div id="fixture-element"></div>\n' +
'<div id="discussion-container"' +
' data-course-name="Fake Course"' +
' data-user-create-comment="true"' +
' data-user-create-subcomment="true"' +
' data-read-only="false"' +
// suppressing Line is too long (4272 characters!)
/* jshint -W101 */
appendSetFixtures(
"<script type=\'text/template\' id=\'thread-list-template\'>\n <div class=\'forum-nav-header\'>\n <button type=\'button\' class=\'forum-nav-browse\' id=\'forum-nav-browse\' aria-haspopup=\'true\'>\n <span class=\'icon fa fa-bars\' aria-hidden=\'true\'></span>\n <span class=\'sr\'>Discussion topics; currently listing: </span>\n <span class=\'forum-nav-browse-current\'>All Discussions</span>\n ▾\n </button>\n <form class=\'forum-nav-search\'>\n <label>\n <span class=\'sr\'>Search all posts</span>\n <input class=\'forum-nav-search-input\' id=\'forum-nav-search\' type=\'text\' placeholder=\'Search all posts\'>\n <span class=\'icon fa fa-search\' aria-hidden=\'true\'></span>\n </label>\n </form>\n </div>\n <div class=\'forum-nav-browse-menu-wrapper\' style=\'display: none\'>\n <form class=\'forum-nav-browse-filter\'>\n <label>\n <span class=\'sr\'>Filter Topics</span>\n <input type=\'text\' class=\'forum-nav-browse-filter-input\' placeholder=\'filter topics\'>\n </label>\n </form>\n <ul class=\'forum-nav-browse-menu\'>\n <li class=\'forum-nav-browse-menu-item forum-nav-browse-menu-all\'>\n <a href=\'#\' class=\'forum-nav-browse-title\'>All Discussions</a>\n </li>\n <li class=\'forum-nav-browse-menu-item forum-nav-browse-menu-following\'>\n <a href=\'#\' class=\'forum-nav-browse-title\'><span class=\'icon fa fa-star\' aria-hidden=\'true\'></span>Posts I'm Following</a>\n </li>\n <li class=\'forum-nav-browse-menu-item\'>\n <a href=\'#\' class=\'forum-nav-browse-title\'>Parent</a>\n <ul class=\'forum-nav-browse-submenu\'>\n <li class=\'forum-nav-browse-menu-item\'>\n <a href=\'#\' class=\'forum-nav-browse-title\'>Target</a>\n <ul class=\'forum-nav-browse-submenu\'>\n <li\n class=\'forum-nav-browse-menu-item\'\n data-discussion-id=\'child\'\n data-cohorted=\'false\'\n >\n <a href=\'#\' class=\'forum-nav-browse-title\'>Child</a>\n </li>\n </ul>\n <li\n class=\'forum-nav-browse-menu-item\'\n data-discussion-id=\'sibling\'\n data-cohorted=\'false\'\n >\n <a href=\'#\' class=\'forum-nav-browse-title\'>Sibling</a>\n </li>\n </ul>\n </li>\n <li\n class=\'forum-nav-browse-menu-item\'\n data-discussion-id=\'other\'\n data-cohorted=\'true\'\n >\n <a href=\'#\' class=\'forum-nav-browse-title\'>Other Category</a>\n </li>\n </ul>\n </div>\n <div class=\'forum-nav-thread-list-wrapper\' id=\'sort-filter-wrapper\' tabindex=\'-1\'>\n <div class=\'forum-nav-refine-bar\'>\n <label class=\'forum-nav-filter-main\'>\n <select class=\'forum-nav-filter-main-control\'>\n <option value=\'all\'>Show all</option>\n <option value=\'unread\'>Unread</option>\n <option value=\'unanswered\'>Unanswered</option>\n <option value=\'flagged\'>Flagged</option>\n </select>\n </label>\n <% if (isCohorted && isPrivilegedUser) { %>\n <label class=\'forum-nav-filter-cohort\'>\n <span class=\'sr\'>Cohort:</span>\n <select class=\'forum-nav-filter-cohort-control\'>\n <option value=\'\'>in all cohorts</option>\n <option value=\'1\'>Cohort1</option>\n <option value=\'2\'>Cohort2</option>\n </select>\n </label>\n <% } %>\n <label class=\'forum-nav-sort\'>\n <select class=\'forum-nav-sort-control\'>\n <option value=\'activity\'>by recent activity</option>\n <option value=\'comments\'>by most activity</option>\n <option value=\'votes\'>by most votes</option>\n </select>\n </label>\n </div>\n </div>\n <div class=\'search-alerts\'></div>\n <ul class=\'forum-nav-thread-list\'></ul>\n</script>");
appendSetFixtures(
'<div id=\'fixture-element\'></div>\n' +
'<div id=\'discussion-container\'' +
' data-course-name=\'Fake Course\'' +
' data-user-create-comment=\'true\'' +
' data-user-create-subcomment=\'true\'' +
' data-read-only=\'false\'' +
'></div>'
);
};
return DiscussionSpecHelper;
})();
}());
}).call(this);
......@@ -577,11 +577,11 @@ class DiscussionUserProfilePage(CoursePage):
def is_browser_on_page(self):
return (
self.q(css='section.discussion-user-threads[data-course-id="{}"]'.format(self.course_id)).present
self.q(css='.discussion-user-threads[data-course-id="{}"]'.format(self.course_id)).present
and
self.q(css='section.user-profile a.learner-profile-link').present
self.q(css='.user-profile .learner-profile-link').present
and
self.q(css='section.user-profile a.learner-profile-link').text[0] == self.username
self.q(css='.user-profile .learner-profile-link').text[0] == self.username
)
@wait_for_js
......@@ -721,7 +721,7 @@ class DiscussionTabHomePage(CoursePage, DiscussionPageMixin):
"""
Returns the new post button.
"""
elements = self.q(css="ol.course-tabs .new-post-btn")
elements = self.q(css=".new-post-btn")
return elements.first if elements.visible and len(elements) == 1 else None
@property
......
;(function(define) {
(function(define) {
'use strict';
define(['jquery', 'backbone'],
function($, Backbone) {
define(
[
'jquery',
'backbone',
'discussion/js/discussion_router',
'common/js/discussion/views/new_post_view'
],
function($, Backbone, DiscussionRouter, NewPostView) {
return function(options) {
var element = options.el,
userInfo = element.data('user-info'),
sortPreference = element.data('sort-preference'),
threads = element.data('threads'),
threadPages = element.data('thread-pages'),
contentInfo = element.data('content-info'),
var userInfo = options.user_info,
sortPreference = options.sort_preference,
threads = options.threads,
threadPages = options.thread_pages,
contentInfo = options.content_info,
user = new window.DiscussionUser(userInfo),
discussion,
courseSettings;
courseSettings,
newPostView,
router;
// TODO: Perhaps eliminate usage of global variables when possible
window.DiscussionUtil.loadRolesFromContainer();
window.DiscussionUtil.loadRoles(options.roles);
window.$$course_id = options.courseId;
window.courseName = element.data('course-name');
window.courseName = options.course_name;
window.DiscussionUtil.setUser(user);
window.user = user;
window.Content.loadContentInfos(contentInfo);
discussion = new window.Discussion(threads, {pages: threadPages, sort: sortPreference});
courseSettings = new window.DiscussionCourseSettings(element.data('course-settings'));
// jshint nonew:false
new window.DiscussionRouter({
discussion: discussion,
course_settings: courseSettings
courseSettings = new window.DiscussionCourseSettings(options.course_settings);
// Create the new post view
newPostView = new NewPostView({
el: $('.new-post-article'),
collection: discussion,
course_settings: courseSettings,
mode: 'tab'
});
Backbone.history.start({
pushState: true,
root: '/courses/' + options.courseId + '/discussion/forum/'
newPostView.render();
// Set up the router to manage the page's history
router = new DiscussionRouter({
courseId: options.courseId,
discussion: discussion,
courseSettings: courseSettings,
newPostView: newPostView
});
router.start();
};
});
}).call(this, define || RequireJS.define);
;(function(define) {
(function(define) {
'use strict';
define(['jquery', 'DiscussionUserProfileView'],
define(['jquery', 'discussion/js/views/discussion_user_profile_view'],
function($, DiscussionUserProfileView) {
return function(options) {
var element = options.el,
threads = element.data('threads'),
userInfo = element.data('user-info'),
page = element.data('page'),
numPages = element.data('num-pages');
var $element = options.$el,
threads = options.threads,
userInfo = options.userInfo,
page = options.page,
numPages = options.numPages;
// Roles are not included in user profile page, but they are not used for anything
window.DiscussionUtil.loadRoles({
'Moderator': [],
'Administrator': [],
'Community TA': []
});
window.$$course_id = element.data('course-id');
window.$$course_id = options.courseId;
window.user = new window.DiscussionUser(userInfo);
// jshint nonew:false
new DiscussionUserProfileView({
el: element,
el: $element,
collection: threads,
page: page,
numPages: numPages
......
/* globals DiscussionThreadListView, DiscussionThreadView, DiscussionUtil, NewPostView, Thread */
(function() {
(function(define) {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
function getSingleThreadRoute(commentable_id, thread_id) {
return commentable_id + '/threads/' + thread_id;
}
if (typeof Backbone !== 'undefined' && Backbone !== null) {
this.DiscussionRouter = (function(_super) {
var allThreadsRoute = '',
singleThreadRoute = getSingleThreadRoute(':forum_name', ':thread_id'), // :forum_name/threads/:thread_id
routes = {};
routes[allThreadsRoute] = 'allThreads';
routes[singleThreadRoute] = 'showThread';
__extends(DiscussionRouter, _super);
function DiscussionRouter() {
var self = this;
this.hideNewPost = function() {
return DiscussionRouter.prototype.hideNewPost.apply(self, arguments);
};
this.showNewPost = function() {
return DiscussionRouter.prototype.showNewPost.apply(self, arguments);
};
this.navigateToAllThreads = function() {
return DiscussionRouter.prototype.navigateToAllThreads.apply(self, arguments);
};
this.navigateToThread = function() {
return DiscussionRouter.prototype.navigateToThread.apply(self, arguments);
};
this.showMain = function() {
return DiscussionRouter.prototype.showMain.apply(self, arguments);
};
this.renderThreadView = function() {
return DiscussionRouter.prototype.renderThreadView.apply(self, arguments);
};
this.setActiveThread = function() {
return DiscussionRouter.prototype.setActiveThread.apply(self, arguments);
};
return DiscussionRouter.__super__.constructor.apply(this, arguments);
}
DiscussionRouter.prototype.routes = routes;
DiscussionRouter.prototype.initialize = function(options) {
var self = this;
this.discussion = options.discussion;
this.course_settings = options.course_settings;
this.nav = new DiscussionThreadListView({
collection: this.discussion,
el: $('.forum-nav'),
courseSettings: this.course_settings
});
this.nav.on('thread:selected', this.navigateToThread);
this.nav.on('thread:removed', this.navigateToAllThreads);
this.nav.on('threads:rendered', this.setActiveThread);
this.nav.on('thread:created', this.navigateToThread);
this.nav.render();
this.newPost = $('.new-post-article');
this.newPostView = new NewPostView({
el: this.newPost,
collection: this.discussion,
course_settings: this.course_settings,
mode: 'tab'
});
this.newPostView.render();
this.listenTo(this.newPostView, 'newPost:cancel', this.hideNewPost);
$('.new-post-btn').bind('click', this.showNewPost);
return $('.new-post-btn').bind('keydown', function(event) {
return DiscussionUtil.activateOnSpace(event, self.showNewPost);
});
};
DiscussionRouter.prototype.allThreads = function() {
this.nav.updateSidebar();
this.nav.goHome();
};
DiscussionRouter.prototype.setActiveThread = function() {
if (this.thread) {
this.nav.setActiveThread(this.thread.get('id'));
}
};
DiscussionRouter.prototype.showThread = function(forum_name, thread_id) {
var self = this;
this.thread = this.discussion.get(thread_id);
if (this.thread) {
this.renderThreadView();
return;
}
// if thread is not loaded yet for some reason - try loading it
DiscussionUtil.safeAjax({
url: DiscussionUtil.urlFor('retrieve_single_thread', forum_name, thread_id)
}).done(function(data) {
// if succeded - proceed normally
self.thread = new Thread(data.content);
self.discussion.add(self.thread);
self.renderThreadView();
}).fail(function(xhr) {
// otherwise display error message and navigate to all threads view
var errorMsg;
if (xhr.status === 404) {
errorMsg = gettext('The thread you selected has been deleted. Please select another thread.');
define(
[
'underscore',
'backbone',
'common/js/discussion/utils',
'common/js/discussion/views/discussion_thread_list_view',
'common/js/discussion/views/discussion_thread_view'
],
function(_, Backbone, DiscussionUtil, DiscussionThreadListView, DiscussionThreadView) {
var DiscussionRouter = Backbone.Router.extend({
routes: {
'': 'allThreads',
':forum_name/threads/:thread_id': 'showThread'
},
initialize: function(options) {
Backbone.Router.prototype.initialize.call(this);
_.bindAll(this, 'allThreads', 'showThread');
this.courseId = options.courseId;
this.discussion = options.discussion;
this.course_settings = options.courseSettings;
this.newPostView = options.newPostView;
this.nav = new DiscussionThreadListView({
collection: this.discussion,
el: $('.forum-nav'),
courseSettings: this.course_settings
});
this.nav.render();
},
start: function() {
var self = this,
$newPostButton = $('.new-post-btn');
this.listenTo(this.newPostView, 'newPost:cancel', this.hideNewPost);
$newPostButton.bind('click', _.bind(this.showNewPost, this));
$newPostButton.bind('keydown', function(event) {
DiscussionUtil.activateOnSpace(event, self.showNewPost);
});
// Automatically navigate when the user selects threads
this.nav.on('thread:selected', _.bind(this.navigateToThread, this));
this.nav.on('thread:removed', _.bind(this.navigateToAllThreads, this));
this.nav.on('threads:rendered', _.bind(this.setActiveThread, this));
this.nav.on('thread:created', _.bind(this.navigateToThread, this));
Backbone.history.start({
pushState: true,
root: '/courses/' + this.courseId + '/discussion/forum/'
});
},
stop: function() {
Backbone.history.stop();
},
allThreads: function() {
this.nav.updateSidebar();
return this.nav.goHome();
},
setActiveThread: function() {
if (this.thread) {
return this.nav.setActiveThread(this.thread.get('id'));
} else {
errorMsg = gettext('We had some trouble loading more responses. Please try again.');
return this.nav.goHome;
}
DiscussionUtil.discussionAlert(gettext('Sorry'), errorMsg);
this.allThreads();
});
};
DiscussionRouter.prototype.renderThreadView = function() {
this.thread.set('unread_comments_count', 0);
this.thread.set('read', true);
this.setActiveThread();
this.showMain();
};
DiscussionRouter.prototype.showMain = function() {
var self = this;
if (this.main) {
this.main.cleanup();
this.main.undelegateEvents();
}
if (!($('.forum-content').is(':visible'))) {
$('.forum-content').fadeIn();
}
if (this.newPost.is(':visible')) {
this.newPost.fadeOut();
}
this.main = new DiscussionThreadView({
el: $('.forum-content'),
model: this.thread,
mode: 'tab',
course_settings: this.course_settings
});
this.main.render();
this.main.on('thread:responses:rendered', function() {
return self.nav.updateSidebar();
});
this.thread.on('thread:thread_type_updated', this.showMain);
};
DiscussionRouter.prototype.navigateToThread = function(thread_id) {
var thread, targetThreadRoute;
thread = this.discussion.get(thread_id);
targetThreadRoute = getSingleThreadRoute(thread.get('commentable_id'), thread_id);
this.navigate(targetThreadRoute, {trigger: true});
};
DiscussionRouter.prototype.navigateToAllThreads = function() {
this.navigate(allThreadsRoute, {trigger: true});
};
DiscussionRouter.prototype.showNewPost = function() {
var self = this;
$('.forum-content').fadeOut({
duration: 200,
complete: function() {
return self.newPost.fadeIn(200).focus();
},
showThread: function(forumName, threadId) {
this.thread = this.discussion.get(threadId);
this.thread.set('unread_comments_count', 0);
this.thread.set('read', true);
this.setActiveThread();
return this.showMain();
},
showMain: function() {
var self = this;
if (this.main) {
this.main.cleanup();
this.main.undelegateEvents();
}
});
};
DiscussionRouter.prototype.hideNewPost = function() {
this.newPost.fadeOut({
duration: 200,
complete: function() {
return $('.forum-content').fadeIn(200).find('.thread-wrapper').focus();
if (!($('.forum-content').is(':visible'))) {
$('.forum-content').fadeIn();
}
if (this.newPostView.$el.is(':visible')) {
this.newPostView.$el.fadeOut();
}
});
};
this.main = new DiscussionThreadView({
el: $('.forum-content'),
model: this.thread,
mode: 'tab',
course_settings: this.course_settings
});
this.main.render();
this.main.on('thread:responses:rendered', function() {
return self.nav.updateSidebar();
});
return this.thread.on('thread:thread_type_updated', this.showMain);
},
navigateToThread: function(threadId) {
var thread;
thread = this.discussion.get(threadId);
return this.navigate('' + (thread.get('commentable_id')) + '/threads/' + threadId, {
trigger: true
});
},
navigateToAllThreads: function() {
return this.navigate('', {
trigger: true
});
},
showNewPost: function() {
var self = this;
return $('.forum-content').fadeOut({
duration: 200,
complete: function() {
return self.newPostView.$el.fadeIn(200).focus();
}
});
},
hideNewPost: function() {
return this.newPostView.$el.fadeOut({
duration: 200,
complete: function() {
return $('.forum-content').fadeIn(200).find('.thread-wrapper')
.focus();
}
});
}
});
return DiscussionRouter;
})(Backbone.Router);
}
}).call(window);
});
}).call(this, define || RequireJS.define);
define(
[
'jquery',
'backbone',
'common/js/spec_helpers/page_helpers',
'common/js/spec_helpers/discussion_spec_helper',
'discussion/js/discussion_board_factory'
],
function($, Backbone, PageHelpers, DiscussionSpecHelper, DiscussionBoardFactory) {
'use strict';
// TODO: re-enable when this doesn't interact badly with other history tests
xdescribe('Discussion Board Factory', function() {
var initializeDiscussionBoardFactory = function() {
DiscussionBoardFactory({
el: $('.discussion-board'),
courseId: 'test_course_id',
course_name: 'Test Course',
user_info: DiscussionSpecHelper.getTestUserInfo(),
roles: DiscussionSpecHelper.getTestRoleInfo(),
sort_preference: null,
threads: [],
thread_pages: [],
content_info: null,
course_settings: {
is_cohorted: false,
allow_anonymous: false,
allow_anonymous_to_peers: false,
cohorts: [],
category_map: {}
}
});
};
beforeEach(function() {
PageHelpers.preventBackboneChangingUrl();
// Install the fixtures
setFixtures(
'<div class="discussion-board">' +
' <div class="forum-nav"></div>' +
'</div>'
);
DiscussionSpecHelper.setUnderscoreFixtures();
});
afterEach(function() {
Backbone.history.stop();
});
it('can render itself', function() {
initializeDiscussionBoardFactory();
expect($('.discussion-board').text()).toContain('All Discussions');
});
});
}
);
define(
[
'underscore',
'jquery',
'backbone',
'common/js/spec_helpers/discussion_spec_helper',
'discussion/js/discussion_profile_page_factory'
],
function(_, $, Backbone, DiscussionSpecHelper, DiscussionProfilePageFactory) {
'use strict';
describe('Discussion Profile Page Factory', function() {
var testCourseId = 'test_course',
initializeDiscussionProfilePageFactory = function(options) {
DiscussionProfilePageFactory(_.extend(
{
courseId: testCourseId,
$el: $('.discussion-user-threads'),
user_info: DiscussionSpecHelper.getTestUserInfo(),
roles: DiscussionSpecHelper.getTestRoleInfo(),
sort_preference: null,
threads: [],
page: 1,
numPages: 5
},
options
));
};
beforeEach(function() {
setFixtures('<div class="discussion-user-threads"></div>');
DiscussionSpecHelper.setUnderscoreFixtures();
});
it('can render itself', function() {
initializeDiscussionProfilePageFactory();
expect($('.discussion-user-threads').text()).toContain('Active Threads');
});
});
}
);
/* globals DiscussionSpecHelper, DiscussionThreadProfileView, DiscussionUserProfileView, URI, DiscussionUtil */
(function() {
'use strict';
describe('DiscussionUserProfileView', function() {
var makeThreads, makeView;
beforeEach(function() {
DiscussionSpecHelper.setUpGlobals();
DiscussionSpecHelper.setUnderscoreFixtures();
return spyOn(DiscussionThreadProfileView.prototype, 'render');
});
makeThreads = function(numThreads) {
return _.map(_.range(numThreads), function(i) {
return {
id: i.toString(),
body: 'dummy body'
};
});
};
makeView = function(threads, page, numPages) {
return new DiscussionUserProfileView({
collection: threads,
page: page,
numPages: numPages
define(
[
'underscore',
'jquery',
'URI',
'common/js/discussion/utils',
'common/js/discussion/views/discussion_thread_profile_view',
'common/js/spec_helpers/discussion_spec_helper',
'discussion/js/views/discussion_user_profile_view'
],
function(_, $, URI, DiscussionUtil, DiscussionThreadProfileView, DiscussionSpecHelper, DiscussionUserProfileView) {
'use strict';
describe('DiscussionUserProfileView', function() {
var makeThreads, makeView;
beforeEach(function() {
DiscussionSpecHelper.setUpGlobals();
DiscussionSpecHelper.setUnderscoreFixtures();
return spyOn(DiscussionThreadProfileView.prototype, 'render');
});
};
describe('thread rendering should be correct', function() {
var checkRender;
checkRender = function(numThreads) {
var threads, view;
threads = makeThreads(numThreads);
view = makeView(threads, 1, 1);
expect(view.$('.discussion').children().length).toEqual(numThreads);
return _.each(threads, function(thread) {
return expect(view.$('#thread_' + thread.id).length).toEqual(1);
makeThreads = function(numThreads) {
return _.map(_.range(numThreads), function(i) {
return {
id: i.toString(),
body: 'dummy body'
};
});
};
it('with no threads', function() {
return checkRender(0);
});
it('with one thread', function() {
return checkRender(1);
});
it('with several threads', function() {
return checkRender(5);
});
});
describe('pagination rendering should be correct', function() {
var baseUri, checkRender, pageInfo;
baseUri = URI(window.location);
pageInfo = function(page) {
return {
url: baseUri.clone().addSearch('page', page).toString(),
number: page
};
makeView = function(threads, page, numPages) {
return new DiscussionUserProfileView({
collection: threads,
page: page,
numPages: numPages
});
};
checkRender = function(params) {
var get_page_number, paginator, view;
view = makeView([], params.page, params.numPages);
paginator = view.$('.discussion-paginator');
expect(paginator.find('.current-page').text()).toEqual(params.page.toString());
expect(paginator.find('.first-page').length).toBe(params.first ? 1 : 0);
expect(paginator.find('.previous-page').length).toBe(params.previous ? 1 : 0);
expect(paginator.find('.previous-ellipses').length).toBe(params.leftdots ? 1 : 0);
expect(paginator.find('.next-page').length).toBe(params.next ? 1 : 0);
expect(paginator.find('.next-ellipses').length).toBe(params.rightdots ? 1 : 0);
expect(paginator.find('.last-page').length).toBe(params.last ? 1 : 0);
get_page_number = function(element) {
return parseInt($(element).text());
describe('thread rendering should be correct', function() {
var checkRender;
checkRender = function(numThreads) {
var threads, view;
threads = makeThreads(numThreads);
view = makeView(threads, 1, 1);
expect(view.$('.discussion').children().length).toEqual(numThreads);
return _.each(threads, function(thread) {
return expect(view.$('#thread_' + thread.id).length).toEqual(1);
});
};
expect(_.map(paginator.find('.lower-page a'), get_page_number)).toEqual(params.lowPages);
return expect(_.map(paginator.find('.higher-page a'), get_page_number)).toEqual(params.highPages);
};
it('for one page', function() {
return checkRender({
page: 1,
numPages: 1,
previous: null,
first: null,
leftdots: false,
lowPages: [],
highPages: [],
rightdots: false,
last: null,
next: null
it('with no threads', function() {
return checkRender(0);
});
});
it('for first page of three (max with no last)', function() {
return checkRender({
page: 1,
numPages: 3,
previous: null,
first: null,
leftdots: false,
lowPages: [],
highPages: [2, 3],
rightdots: false,
last: null,
next: 2
it('with one thread', function() {
return checkRender(1);
});
});
it('for first page of four (has last but no dots)', function() {
return checkRender({
page: 1,
numPages: 4,
previous: null,
first: null,
leftdots: false,
lowPages: [],
highPages: [2, 3],
rightdots: false,
last: 4,
next: 2
it('with several threads', function() {
return checkRender(5);
});
});
it('for first page of five (has dots)', function() {
return checkRender({
page: 1,
numPages: 5,
previous: null,
first: null,
leftdots: false,
lowPages: [],
highPages: [2, 3],
rightdots: true,
last: 5,
next: 2
describe('pagination rendering should be correct', function() {
var baseUri, checkRender, pageInfo;
baseUri = URI(window.location);
pageInfo = function(page) {
return {
url: baseUri.clone().addSearch('page', page).toString(),
number: page
};
};
checkRender = function(params) {
var getPageNumber, paginator, view;
view = makeView([], params.page, params.numPages);
paginator = view.$('.discussion-paginator');
expect(paginator.find('.current-page').text()).toEqual(params.page.toString());
expect(paginator.find('.first-page').length).toBe(params.first ? 1 : 0);
expect(paginator.find('.previous-page').length).toBe(params.previous ? 1 : 0);
expect(paginator.find('.previous-ellipses').length).toBe(params.leftdots ? 1 : 0);
expect(paginator.find('.next-page').length).toBe(params.next ? 1 : 0);
expect(paginator.find('.next-ellipses').length).toBe(params.rightdots ? 1 : 0);
expect(paginator.find('.last-page').length).toBe(params.last ? 1 : 0);
getPageNumber = function(element) {
return parseInt($(element).text(), 10);
};
expect(_.map(paginator.find('.lower-page a'), getPageNumber)).toEqual(params.lowPages);
return expect(_.map(paginator.find('.higher-page a'), getPageNumber)).toEqual(params.highPages);
};
it('for one page', function() {
return checkRender({
page: 1,
numPages: 1,
previous: null,
first: null,
leftdots: false,
lowPages: [],
highPages: [],
rightdots: false,
last: null,
next: null
});
});
});
it('for last page of three (max with no first)', function() {
return checkRender({
page: 3,
numPages: 3,
previous: 2,
first: null,
leftdots: false,
lowPages: [1, 2],
highPages: [],
rightdots: false,
last: null,
next: null
it('for first page of three (max with no last)', function() {
return checkRender({
page: 1,
numPages: 3,
previous: null,
first: null,
leftdots: false,
lowPages: [],
highPages: [2, 3],
rightdots: false,
last: null,
next: 2
});
});
});
it('for last page of four (has first but no dots)', function() {
return checkRender({
page: 4,
numPages: 4,
previous: 3,
first: 1,
leftdots: false,
lowPages: [2, 3],
highPages: [],
rightdots: false,
last: null,
next: null
it('for first page of four (has last but no dots)', function() {
return checkRender({
page: 1,
numPages: 4,
previous: null,
first: null,
leftdots: false,
lowPages: [],
highPages: [2, 3],
rightdots: false,
last: 4,
next: 2
});
});
});
it('for last page of five (has dots)', function() {
return checkRender({
page: 5,
numPages: 5,
previous: 4,
first: 1,
leftdots: true,
lowPages: [3, 4],
highPages: [],
rightdots: false,
last: null,
next: null
it('for first page of five (has dots)', function() {
return checkRender({
page: 1,
numPages: 5,
previous: null,
first: null,
leftdots: false,
lowPages: [],
highPages: [2, 3],
rightdots: true,
last: 5,
next: 2
});
});
});
it('for middle page of five (max with no first/last)', function() {
return checkRender({
page: 3,
numPages: 5,
previous: 2,
first: null,
leftdots: false,
lowPages: [1, 2],
highPages: [4, 5],
rightdots: false,
last: null,
next: 4
it('for last page of three (max with no first)', function() {
return checkRender({
page: 3,
numPages: 3,
previous: 2,
first: null,
leftdots: false,
lowPages: [1, 2],
highPages: [],
rightdots: false,
last: null,
next: null
});
});
});
it('for middle page of seven (has first/last but no dots)', function() {
return checkRender({
page: 4,
numPages: 7,
previous: 3,
first: 1,
leftdots: false,
lowPages: [2, 3],
highPages: [5, 6],
rightdots: false,
last: 7,
next: 5
it('for last page of four (has first but no dots)', function() {
return checkRender({
page: 4,
numPages: 4,
previous: 3,
first: 1,
leftdots: false,
lowPages: [2, 3],
highPages: [],
rightdots: false,
last: null,
next: null
});
});
});
it('for middle page of nine (has dots)', function() {
return checkRender({
page: 5,
numPages: 9,
previous: 4,
first: 1,
leftdots: true,
lowPages: [3, 4],
highPages: [6, 7],
rightdots: true,
last: 9,
next: 6
it('for last page of five (has dots)', function() {
return checkRender({
page: 5,
numPages: 5,
previous: 4,
first: 1,
leftdots: true,
lowPages: [3, 4],
highPages: [],
rightdots: false,
last: null,
next: null
});
});
it('for middle page of five (max with no first/last)', function() {
return checkRender({
page: 3,
numPages: 5,
previous: 2,
first: null,
leftdots: false,
lowPages: [1, 2],
highPages: [4, 5],
rightdots: false,
last: null,
next: 4
});
});
it('for middle page of seven (has first/last but no dots)', function() {
return checkRender({
page: 4,
numPages: 7,
previous: 3,
first: 1,
leftdots: false,
lowPages: [2, 3],
highPages: [5, 6],
rightdots: false,
last: 7,
next: 5
});
});
it('for middle page of nine (has dots)', function() {
return checkRender({
page: 5,
numPages: 9,
previous: 4,
first: 1,
leftdots: true,
lowPages: [3, 4],
highPages: [6, 7],
rightdots: true,
last: 9,
next: 6
});
});
});
});
describe('pagination interaction', function() {
beforeEach(function() {
var deferred;
this.view = makeView(makeThreads(3), 1, 2);
deferred = $.Deferred();
return spyOn($, 'ajax').and.returnValue(deferred);
});
it('causes updated rendering', function() {
$.ajax.and.callFake(function(params) {
params.success({
discussion_data: [
{
id: 'on_page_42',
body: 'dummy body'
describe('pagination interaction', function() {
beforeEach(function() {
var deferred;
this.view = makeView(makeThreads(3), 1, 2);
deferred = $.Deferred();
return spyOn($, 'ajax').and.returnValue(deferred);
});
it('causes updated rendering', function() {
$.ajax.and.callFake(function(params) {
params.success({
discussion_data: [
{
id: 'on_page_42',
body: 'dummy body'
}
],
page: 42,
num_pages: 99
});
return {
always: function() {
}
],
page: 42,
num_pages: 99
};
});
return {
always: function() {
}
};
this.view.$('.discussion-pagination a').first().click();
expect(this.view.$('.current-page').text()).toEqual('42');
return expect(this.view.$('.last-page').text()).toEqual('99');
});
this.view.$('.discussion-pagination a').first().click();
expect(this.view.$('.current-page').text()).toEqual('42');
return expect(this.view.$('.last-page').text()).toEqual('99');
});
it('handles AJAX errors', function() {
spyOn(DiscussionUtil, 'discussionAlert');
$.ajax.and.callFake(function(params) {
params.error();
return {
always: function() {
}
};
it('handles AJAX errors', function() {
spyOn(DiscussionUtil, 'discussionAlert');
$.ajax.and.callFake(function(params) {
params.error();
return {
always: function() {
}
};
});
this.view.$('.discussion-pagination a').first().click();
return expect(DiscussionUtil.discussionAlert).toHaveBeenCalled();
});
this.view.$('.discussion-pagination a').first().click();
return expect(DiscussionUtil.discussionAlert).toHaveBeenCalled();
});
});
});
}).call(this);
/* globals Discussion, DiscussionThreadProfileView, DiscussionUtil, URI */
(function() {
(function(define) {
'use strict';
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
if (typeof Backbone !== 'undefined' && Backbone !== null) {
this.DiscussionUserProfileView = (function(_super) {
__extends(DiscussionUserProfileView, _super);
function DiscussionUserProfileView() {
var self = this;
this.render = function() {
return DiscussionUserProfileView.prototype.render.apply(self, arguments);
};
return DiscussionUserProfileView.__super__.constructor.apply(this, arguments);
}
DiscussionUserProfileView.prototype.events = {
'click .discussion-paginator a': 'changePage'
};
define([
'underscore',
'jquery',
'backbone',
'gettext',
'URI',
'edx-ui-toolkit/js/utils/html-utils',
'common/js/components/utils/view_utils',
'common/js/discussion/discussion',
'common/js/discussion/utils',
'common/js/discussion/views/discussion_thread_profile_view',
'text!discussion/templates/user-profile.underscore',
'text!common/templates/discussion/pagination.underscore'
],
function(_, $, Backbone, gettext, URI, HtmlUtils, ViewUtils, Discussion, DiscussionUtil,
DiscussionThreadProfileView, userProfileTemplate, paginationTemplate) {
var DiscussionUserProfileView = Backbone.View.extend({
events: {
'click .discussion-paginator a': 'changePage'
},
DiscussionUserProfileView.prototype.initialize = function(options) {
DiscussionUserProfileView.__super__.initialize.call(this);
this.page = options.page;
this.numPages = options.numPages;
this.discussion = new Discussion();
this.discussion.on('reset', this.render);
return this.discussion.reset(this.collection, {
silent: false
});
};
initialize: function(options) {
Backbone.View.prototype.initialize.call(this);
this.page = options.page;
this.numPages = options.numPages;
this.discussion = new Discussion();
this.discussion.on('reset', _.bind(this.render, this));
this.discussion.reset(this.collection, {silent: false});
},
DiscussionUserProfileView.prototype.render = function() {
var baseUri, pageUrlFunc, paginationParams,
self = this;
this.$el.html(_.template($('#user-profile-template').html())({
threads: this.discussion.models
}));
this.discussion.map(function(thread) {
return new DiscussionThreadProfileView({
el: self.$('article#thread_' + thread.id),
model: thread
}).render();
});
baseUri = URI(window.location).removeSearch('page');
pageUrlFunc = function(page) {
return baseUri.clone().addSearch('page', page);
};
paginationParams = DiscussionUtil.getPaginationParams(this.page, this.numPages, pageUrlFunc);
this.$el.find('.discussion-pagination')
.html(_.template($('#pagination-template').html())(paginationParams));
};
DiscussionUserProfileView.prototype.changePage = function(event) {
var url,
self = this;
event.preventDefault();
url = $(event.target).attr('href');
return DiscussionUtil.safeAjax({
$elem: this.$el,
$loading: $(event.target),
takeFocus: true,
url: url,
type: 'GET',
dataType: 'json',
success: function(response) {
self.page = response.page;
self.numPages = response.num_pages;
self.discussion.reset(response.discussion_data, {
silent: false
});
history.pushState({}, '', url);
return $('html, body').animate({
scrollTop: 0
render: function() {
var self = this,
baseUri = URI(window.location).removeSearch('page'),
pageUrlFunc,
paginationParams;
HtmlUtils.setHtml(this.$el, HtmlUtils.template(userProfileTemplate)({
threads: self.discussion.models
}));
this.discussion.map(function(thread) {
var view = new DiscussionThreadProfileView({
el: self.$('article#thread_' + thread.id),
model: thread
});
},
error: function() {
return DiscussionUtil.discussionAlert(
gettext('Sorry'),
gettext('We had some trouble loading the page you requested. Please try again.')
);
}
});
};
view.render();
return view;
});
pageUrlFunc = function(page) {
return baseUri.clone().addSearch('page', page).toString();
};
paginationParams = DiscussionUtil.getPaginationParams(this.page, this.numPages, pageUrlFunc);
HtmlUtils.setHtml(
this.$el.find('.discussion-pagination'),
HtmlUtils.template(paginationTemplate)(paginationParams)
);
return this;
},
changePage: function(event) {
var self = this,
url;
event.preventDefault();
url = $(event.target).attr('href');
DiscussionUtil.safeAjax({
$elem: this.$el,
$loading: $(event.target),
takeFocus: true,
url: url,
type: 'GET',
dataType: 'json',
success: function(response) {
self.page = response.page;
self.numPages = response.num_pages;
self.discussion.reset(response.discussion_data, {silent: false});
history.pushState({}, '', url);
ViewUtils.setScrollTop(0);
},
error: function() {
DiscussionUtil.discussionAlert(
gettext('Sorry'),
gettext('We had some trouble loading the page you requested. Please try again.')
);
}
});
}
});
return DiscussionUserProfileView;
})(Backbone.View);
}
}).call(window);
});
}).call(this, define || RequireJS.define);
<h2><%- gettext("Active Threads") %></h2>
<section class="discussion">
<% _.each(threads, function(thread) { %>
<article class="discussion-thread" id="thread_<%= thread.id %>"/>
<article class="discussion-thread" id="thread_<%- thread.id %>"/>
<% }); %>
</section>
<section class="discussion-pagination"/>
......@@ -9,9 +9,9 @@
from django.utils.translation import ugettext as _
from django.template.defaultfilters import escapejs
from django.core.urlresolvers import reverse
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
from django_comment_client.permissions import has_permission
from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string
%>
<%block name="bodyclass">discussion</%block>
......@@ -21,44 +21,58 @@ from openedx.core.djangolib.js_utils import (
<%include file="../discussion/_js_head_dependencies.html" />
</%block>
<%block name="js_extra">
## Enable fast preview to fix discussion MathJax rendering bug when page first loads.
<%include file="/discussion/_js_body_dependencies.html" args="disable_fast_preview=False"/>
<%static:js group='discussion'/>
<script type="text/javascript">
RequireJS.define('DiscussionBoardFactory', [], function() {return window['DiscussionBoardFactory'];});
</script>
<%block name="base_js_dependencies">
## Enable fast preview to fix discussion MathJax rendering bug when page first loads.
<%include file="/discussion/_js_body_dependencies.html" args="disable_fast_preview=False"/>
</%block>
<%block name="js_extra">
<%static:require_module module_name="discussion/js/discussion_board_factory" class_name="DiscussionBoardFactory">
DiscussionBoardFactory({
courseId: '${unicode(course.id) | n, js_escaped_string}',
el: $(".discussion-board")
});
DiscussionBoardFactory({
courseId: '${unicode(course.id) | n, js_escaped_string}',
$el: $(".discussion-board"),
user_info: ${user_info | n, dump_js_escaped_json},
roles: ${roles | n, dump_js_escaped_json},
sort_preference: '${sort_preference | n, js_escaped_string}',
threads: ${threads | n, dump_js_escaped_json},
thread_pages: '${thread_pages | n, js_escaped_string}',
content_info: ${annotated_content_info | n, dump_js_escaped_json},
course_name: '${course.display_name_with_default | n, js_escaped_string}',
course_settings: ${course_settings | n, dump_js_escaped_json}
});
</%static:require_module>
</%block>
<%include file="/discussion/_discussion_course_navigation.html" args="active_page='discussion'" />
<%include file="../courseware/course_navigation.html" args="active_page='discussion'" />
<%block name="content">
<section class="discussion discussion-board container" id="discussion-container"
data-roles="${roles}"
data-course-id="${course_id}"
data-course-name="${course.display_name_with_default}"
data-user-info="${user_info}"
data-user-create-comment="${can_create_comment}"
data-user-create-subcomment="${can_create_subcomment}"
data-read-only="false"
data-threads="${threads}"
data-thread-pages="${thread_pages}"
data-content-info="${annotated_content_info}"
data-sort-preference="${sort_preference}"
data-flag-moderator="${flag_moderator}"
data-user-cohort-id="${user_cohort}"
data-course-settings="${course_settings}">
<div class="discussion-body">
<div class="forum-nav" role="complementary" aria-label="${_("Discussion thread list")}"></div>
<div class="discussion-column" id="discussion-column">
<main id="main" aria-label="Content" tabindex="-1">
data-user-cohort-id="${user_cohort}">
<header class="page-header has-secondary">
<div class="page-header-main">
<div class="sr-is-focusable" tabindex="-1"></div>
<h2 class="hd hd-2 page-title">${_("Discussion")}</h2>
</div>
<div class="page-header-secondary">
% if has_permission(user, 'create_thread', course.id):
<div class="form-actions">
<button class="btn btn-small new-post-btn">${_("Add a Post")}</button>
</div>
% endif
</div>
</header>
<div class="page-content">
<div class="discussion-body layout layout-1t2t">
<aside class="forum-nav layout-col layout-col-a" role="complementary" aria-label="${_("Discussion thread list")}">
</aside>
<main id="main" aria-label="Content" tabindex="-1" class="discussion-column layout-col layout-col-b">
<article class="new-post-article" style="display: none" tabindex="-1" aria-label="${_("New topic form")}"></article>
<div class="forum-content"></div>
</main>
......
<%inherit file="../main.html" />
## mako
<%! main_css = "style-discussion-main" %>
<%page expression_filter="h"/>
<%inherit file="../main.html" />
<%namespace name='static' file='../static_content.html'/>
<%!
from django.utils.translation import ugettext as _
from django.template.defaultfilters import escapejs
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
%>
<%block name="bodyclass">discussion</%block>
<%block name="bodyclass">discussion-user-profile</%block>
<%block name="pagetitle">${_("Discussion - {course_number}").format(course_number=course.display_number_with_default)}</%block>
<%block name="headextra">
<%static:css group='style-course-vendor'/>
<%static:css group='style-course'/>
<%include file="_js_head_dependencies.html" />
</%block>
<%block name="js_extra">
<%include file="_js_body_dependencies.html" />
<%static:js group='discussion'/>
<%static:require_module module_name="discussion/js/discussion_profile_page_factory" class_name="DiscussionProfilePageFactory">
<%
profile_page_context = {
'courseId': unicode(course.id),
'courseName': course.display_name_with_default,
'userInfo': user_info,
'threads': threads,
'page': page,
'numPages': num_pages,
}
%>
DiscussionProfilePageFactory(_.extend(
{
$el: $('.discussion-user-threads')
},
${profile_page_context | n, dump_js_escaped_json}
));
</%static:require_module>
</%block>
<%include file="../courseware/course_navigation.html" args="active_page='discussion'" />
<section class="container">
<div class="course-wrapper">
<section class="user-profile">
<nav aria-label="${_('User Profile')}">
<article class="sidebar-module discussion-sidebar">
<%include file="_user_profile.html" />
</article>
</nav>
</section>
<section class="course-content container discussion-user-threads" data-course-id="${course.id}" data-course-name="${course.display_name_with_default}" data-threads="${threads}" data-user-info="${user_info}" data-page="${page}" data-num-pages="${num_pages}"/>
</div>
<header class="page-header">
<div class="page-header-main">
<div class="sr-is-focusable" tabindex="-1"></div>
<h2 class="hd hd-2 page-title">${_("Discussion")}</h2>
</div>
</header>
<div class="page-content">
<div class="layout layout-1t2t">
<aside class="forum-nav layout-col layout-col-a" role="complementary" aria-label="${_("Discussion thread list")}">
<nav class="user-profile" aria-label="${_('User Profile')}">
<article class="sidebar-module discussion-sidebar">
<%include file="_user_profile.html" />
</article>
</nav>
</aside>
<main id="main" aria-label="Content" tabindex="-1" class="discussion-column layout-col layout-col-b">
<div class="course-content discussion-user-threads" data-course-id="${course.id}"
data-course-name="${course.display_name_with_default}"
data-threads="${threads}" data-user-info="${user_info}"
data-page="${page}" data-num-pages="${num_pages}">
</div>
</main>
</div>
</div>
</section>
<%include file="_underscore_templates.html" />
## mako
<%! main_css = "style-discussion-main" %>
<%! from django.utils.translation import ugettext as _ %>
<%page expression_filter="h"/>
<%inherit file="../main.html" />
<h1>${_("We're sorry")}</h1>
<p>${_("The forums are currently undergoing maintenance. We'll have them back up shortly!")}</p>
<%block name="bodyclass">discussion</%block>
<%block name="headextra">
<%include file="../discussion/_js_head_dependencies.html" />
</%block>
<%block name="content">
<h2>${_("Discussion unavailable")}</h2>
<div class="alert alert-error" role="alert" aria-labelledby="alert-title-error" tabindex="-1">
<span class="icon alert-icon fa fa-exclamation-triangle" aria-hidden="true"></span>
<div class="alert-message-with-action">
<p class="alert-copy">
${_("The discussions are currently undergoing maintenance. We'll have them back up shortly!")}
</p>
</div>
</div>
</%block>
## mako
<%! main_css = "style-discussion-main" %>
<%page expression_filter="h"/>
<%inherit file="../main.html" />
<%namespace name='static' file='../static_content.html'/>
<%!
from django.utils.translation import ugettext as _
from django.template.defaultfilters import escapejs
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
%>
<%block name="bodyclass">discussion-user-profile</%block>
<%block name="pagetitle">${_("Discussion - {course_number}").format(course_number=course.display_number_with_default)}</%block>
<%block name="headextra">
<%include file="_js_head_dependencies.html" />
</%block>
<%block name="js_extra">
<%include file="_js_body_dependencies.html" />
<%static:js group='discussion'/>
<script type="text/javascript">
RequireJS.define('DiscussionUserProfileView', [], function() {return window['DiscussionUserProfileView'];});
</script>
<%static:require_module module_name="discussion/js/discussion_profile_page_factory" class_name="DiscussionProfilePageFactory">
DiscussionProfilePageFactory({
courseId: '${unicode(course.id) | n, js_escaped_string}',
el: $(".discussion-user-threads")
});
</%static:require_module>
</%block>
<%include file="../courseware/course_navigation.html" args="active_page='discussion'" />
<section class="container">
<div class="forum-nav">
<section class="user-profile">
<nav aria-label="${_('User Profile')}">
<article class="sidebar-module discussion-sidebar">
<%include file="_user_profile.html" />
</article>
</nav>
</section>
</div>
<div class="discussion-column" id="discussion-column">
<main id="main" aria-label="Content" tabindex="-1">
<section class="course-content container discussion-user-threads" data-course-id="${course.id}"
data-course-name="${course.display_name_with_default}"
data-threads="${threads}" data-user-info="${user_info}"
data-page="${page}" data-num-pages="${num_pages}"/>
</main>
</div>
</section>
<%include file="_underscore_templates.html" />
......@@ -459,7 +459,7 @@ class SingleCohortedThreadTestCase(CohortedTestCase):
html = response.content
# Verify that the group name is correctly included in the HTML
self.assertRegexpMatches(html, r'&#34;group_name&#34;: &#34;student_cohort&#34;')
self.assertRegexpMatches(html, r'"group_name": "student_cohort"')
@patch('lms.lib.comment_client.utils.requests.request', autospec=True)
......@@ -812,7 +812,7 @@ class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdT
self.client.login(username=user.username, password='test')
return self.client.get(
reverse(views.forum_form_discussion, args=[unicode(self.course.id)]),
reverse("discussion.views.forum_form_discussion", args=[unicode(self.course.id)]),
data=request_data,
**headers
)
......@@ -1147,10 +1147,10 @@ class UserProfileTestCase(UrlResetMixin, ModuleStoreTestCase):
self.assertRegexpMatches(html, r'data-num-pages="1"')
self.assertRegexpMatches(html, r'<span>1</span> discussion started')
self.assertRegexpMatches(html, r'<span>2</span> comments')
self.assertRegexpMatches(html, r'&#34;id&#34;: &#34;{}&#34;'.format(self.TEST_THREAD_ID))
self.assertRegexpMatches(html, r'&#34;title&#34;: &#34;{}&#34;'.format(self.TEST_THREAD_TEXT))
self.assertRegexpMatches(html, r'&#34;body&#34;: &#34;{}&#34;'.format(self.TEST_THREAD_TEXT))
self.assertRegexpMatches(html, r'&#34;username&#34;: &#34;{}&#34;'.format(self.student.username))
self.assertRegexpMatches(html, r'&#39;id&#39;: &#39;{}&#39;'.format(self.TEST_THREAD_ID))
self.assertRegexpMatches(html, r'&#39;title&#39;: &#39;{}&#39;'.format(self.TEST_THREAD_TEXT))
self.assertRegexpMatches(html, r'&#39;body&#39;: &#39;{}&#39;'.format(self.TEST_THREAD_TEXT))
self.assertRegexpMatches(html, r'&#39;username&#39;: u&#39;{}&#39;'.format(self.student.username))
def check_ajax(self, mock_request, **params):
response = self.get_response(mock_request, params, HTTP_X_REQUESTED_WITH="XMLHttpRequest")
......@@ -1360,6 +1360,7 @@ class ForumDiscussionXSSTestCase(UrlResetMixin, ModuleStoreTestCase):
"""
Test that XSS attack is prevented
"""
mock_user.return_value.to_dict.return_value = {}
reverse_url = "%s%s" % (reverse(
"discussion.views.forum_form_discussion",
kwargs={"course_id": unicode(self.course.id)}), '/forum_form_discussion')
......@@ -1377,7 +1378,7 @@ class ForumDiscussionXSSTestCase(UrlResetMixin, ModuleStoreTestCase):
Test that XSS attack is prevented
"""
mock_threads.return_value = [], 1, 1
mock_from_django_user.return_value = Mock()
mock_from_django_user.return_value.to_dict.return_value = {}
mock_request.side_effect = make_mock_request_impl(course=self.course, text='dummy')
url = reverse('discussion.views.user_profile',
......
......@@ -3,7 +3,6 @@ Views handling read (GET) requests for the Discussion tab and inline discussions
"""
from functools import wraps
import json
import logging
from django.contrib.auth.decorators import login_required
......@@ -248,9 +247,9 @@ def forum_form_discussion(request, course_key):
'course': course,
#'recent_active_threads': recent_active_threads,
'staff_access': bool(has_access(request.user, 'staff', course)),
'threads': json.dumps(threads),
'threads': threads,
'thread_pages': query_params['num_pages'],
'user_info': json.dumps(user_info, default=lambda x: None),
'user_info': user_info,
'can_create_comment': has_permission(request.user, "create_comment", course.id),
'can_create_subcomment': has_permission(request.user, "create_sub_comment", course.id),
'can_create_thread': has_permission(request.user, "create_thread", course.id),
......@@ -258,16 +257,16 @@ def forum_form_discussion(request, course_key):
has_permission(request.user, 'openclose_thread', course.id) or
has_access(request.user, 'staff', course)
),
'annotated_content_info': json.dumps(annotated_content_info),
'annotated_content_info': annotated_content_info,
'course_id': course.id.to_deprecated_string(),
'roles': json.dumps(utils.get_role_ids(course_key)),
'roles': utils.get_role_ids(course_key),
'is_moderator': has_permission(request.user, "see_all_cohorts", course_key),
'cohorts': course_settings["cohorts"], # still needed to render _thread_list_template
'user_cohort': user_cohort_id, # read from container in NewPostView
'is_course_cohorted': is_course_cohorted(course_key), # still needed to render _thread_list_template
'sort_preference': user.default_sort_key,
'category_map': course_settings["category_map"],
'course_settings': json.dumps(course_settings),
'course_settings': course_settings,
'disable_courseware_js': True,
'uses_pattern_library': True,
}
......@@ -360,17 +359,17 @@ def single_thread(request, course_key, discussion_id, thread_id):
'discussion_id': discussion_id,
'csrf': csrf(request)['csrf_token'],
'init': '', # TODO: What is this?
'user_info': json.dumps(user_info),
'user_info': user_info,
'can_create_comment': has_permission(request.user, "create_comment", course.id),
'can_create_subcomment': has_permission(request.user, "create_sub_comment", course.id),
'can_create_thread': has_permission(request.user, "create_thread", course.id),
'annotated_content_info': json.dumps(annotated_content_info),
'annotated_content_info': annotated_content_info,
'course': course,
#'recent_active_threads': recent_active_threads,
'course_id': course.id.to_deprecated_string(), # TODO: Why pass both course and course.id to template?
'thread_id': thread_id,
'threads': json.dumps(threads),
'roles': json.dumps(utils.get_role_ids(course_key)),
'threads': threads,
'roles': utils.get_role_ids(course_key),
'is_moderator': is_moderator,
'thread_pages': query_params['num_pages'],
'is_course_cohorted': is_course_cohorted(course_key),
......@@ -382,7 +381,7 @@ def single_thread(request, course_key, discussion_id, thread_id):
'user_cohort': user_cohort,
'sort_preference': cc_user.default_sort_key,
'category_map': course_settings["category_map"],
'course_settings': json.dumps(course_settings),
'course_settings': course_settings,
'disable_courseware_js': True,
'uses_pattern_library': True,
}
......@@ -433,7 +432,7 @@ def user_profile(request, course_key, user_id):
'discussion_data': threads,
'page': query_params['page'],
'num_pages': query_params['num_pages'],
'annotated_content_info': json.dumps(annotated_content_info),
'annotated_content_info': annotated_content_info,
})
else:
django_user = User.objects.get(id=user_id)
......@@ -442,9 +441,9 @@ def user_profile(request, course_key, user_id):
'user': request.user,
'django_user': django_user,
'profiled_user': profiled_user.to_dict(),
'threads': json.dumps(threads),
'user_info': json.dumps(user_info, default=lambda x: None),
'annotated_content_info': json.dumps(annotated_content_info),
'threads': threads,
'user_info': user_info,
'annotated_content_info': annotated_content_info,
'page': query_params['page'],
'num_pages': query_params['num_pages'],
'learner_profile_page_url': reverse('learner_profile', kwargs={'username': django_user.username}),
......@@ -452,7 +451,7 @@ def user_profile(request, course_key, user_id):
'uses_pattern_library': True,
}
return render_to_response('discussion/user_profile.html', context)
return render_to_response('discussion/discussion_profile_page.html', context)
except User.DoesNotExist:
raise Http404
......@@ -531,9 +530,9 @@ def followed_threads(request, course_key, user_id):
'user': request.user,
'django_user': User.objects.get(id=user_id),
'profiled_user': profiled_user.to_dict(),
'threads': json.dumps(paginated_results.collection),
'user_info': json.dumps(user_info),
'annotated_content_info': json.dumps(annotated_content_info),
'threads': paginated_results.collection,
'user_info': user_info,
'annotated_content_info': annotated_content_info,
# 'content': content,
}
......
......@@ -24,10 +24,10 @@ class GroupIdAssertionMixin(object):
def _assert_html_response_contains_group_info(self, response):
group_info = {"group_id": None, "group_name": None}
match = re.search(r'&#34;group_id&#34;: ([\d]*)', response.content)
match = re.search(r'"group_id": (\d*),', response.content)
if match and match.group(1) != '':
group_info["group_id"] = int(match.group(1))
match = re.search(r'&#34;group_name&#34;: &#34;([^&]*)&#34;', response.content)
match = re.search(r'"group_name": "(\w*)",', response.content)
if match:
group_info["group_name"] = match.group(1)
self._assert_thread_contains_group_info(group_info)
......
define([
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/views/team_discussion',
'underscore',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/discussion_spec_helper',
'teams/js/spec_helpers/team_spec_helpers',
'xmodule_js/common_static/common/js/spec_helpers/discussion_spec_helper'
], function(_, AjaxHelpers, TeamDiscussionView, TeamSpecHelpers, DiscussionSpecHelper) {
'teams/js/views/team_discussion'
], function(_, AjaxHelpers, DiscussionSpecHelper, TeamSpecHelpers, TeamDiscussionView) {
'use strict';
xdescribe('TeamDiscussionView', function() {
var discussionView, createDiscussionView, createPost, expandReplies, postReply;
......@@ -115,7 +117,7 @@ define([
body: reply,
comments_count: 1
}),
'annotated_content_info': TeamSpecHelpers.createAnnotatedContentInfo()
annotated_content_info: TeamSpecHelpers.createAnnotatedContentInfo()
});
};
......@@ -202,8 +204,7 @@ define([
it('cannot move a new thread to a different topic', function() {
var requests = AjaxHelpers.requests(this),
view = createDiscussionView(requests),
postTopicButton;
view = createDiscussionView(requests);
createPost(requests, view);
expandReplies(requests, view);
view.$('.action-more .icon').first().click();
......
define([
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/models/team',
'teams/js/views/team_profile', 'teams/js/spec_helpers/team_spec_helpers',
'xmodule_js/common_static/common/js/spec_helpers/discussion_spec_helper'
], function(_, AjaxHelpers, TeamModel, TeamProfileView, TeamSpecHelpers, DiscussionSpecHelper) {
'underscore',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/discussion_spec_helper',
'teams/js/spec_helpers/team_spec_helpers',
'teams/js/models/team',
'teams/js/views/team_profile'
], function(_, AjaxHelpers, DiscussionSpecHelper, TeamSpecHelpers, TeamModel, TeamProfileView) {
'use strict';
describe('TeamProfileView', function() {
var profileView, createTeamProfileView, createTeamModelData, clickLeaveTeam,
......@@ -10,11 +13,11 @@ define([
leaveTeamLinkSelector = '.leave-team-link',
DEFAULT_MEMBERSHIP = [
{
'user': {
'username': TeamSpecHelpers.testUser,
'profile_image': {
'has_image': true,
'image_url_medium': '/image-url'
user: {
username: TeamSpecHelpers.testUser,
profile_image: {
has_image: true,
image_url_medium: '/image-url'
}
}
}
......@@ -198,7 +201,7 @@ define([
it('shows correct error messages', function() {
var requests = AjaxHelpers.requests(this);
var verifyErrorMessage = function(requests, errorMessage, expectedMessage) {
var verifyErrorMessage = function(errorMessage, expectedMessage) {
var view = createTeamProfileView(
requests, {country: 'US', language: 'en', membership: DEFAULT_MEMBERSHIP}
);
......@@ -212,22 +215,19 @@ define([
// verify user_message
verifyErrorMessage(
requests,
JSON.stringify({'user_message': "can't remove user from team"}),
JSON.stringify({user_message: "can't remove user from team"}),
"can't remove user from team"
);
// verify generic error message
verifyErrorMessage(
requests,
'',
'An error occurred. Try again.'
);
// verify error message when json parsing succeeded but error message format is incorrect
verifyErrorMessage(
requests,
JSON.stringify({'blah': "can't remove user from team"}),
JSON.stringify({blah: "can't remove user from team"}),
'An error occurred. Try again.'
);
});
......
......@@ -3,7 +3,7 @@
*/
(function(define) {
'use strict';
define(['backbone', 'underscore', 'gettext', 'DiscussionModuleView'],
define(['backbone', 'underscore', 'gettext', 'common/js/discussion/discussion_module_view'],
function(Backbone, _, gettext, DiscussionModuleView) {
var TeamDiscussionView = Backbone.View.extend({
initialize: function() {
......
......@@ -32,10 +32,6 @@ from openedx.core.djangolib.js_utils import (
<%block name="js_extra">
<%include file="../discussion/_js_body_dependencies.html" />
<%static:js group='discussion'/>
<script type="text/javascript">
RequireJS.define('DiscussionModuleView', [], function() {return window['DiscussionModuleView'];});
</script>
<%static:require_module module_name="teams/js/teams_tab_factory" class_name="TeamsTabFactory">
TeamsTabFactory({
......
../djangoapps/discussion/static/discussion
\ No newline at end of file
......@@ -20,7 +20,7 @@ define(['jquery', 'logger', 'js/courseware/courseware_factory'], function($, Log
$('.internal-link').click();
expect(Logger.log).toHaveBeenCalledWith('edx.ui.lms.link_clicked', {
target_url: 'http://' + window.location.host + '/some/internal/link',
current_url: 'http://' + window.location.host + '/context.html'
current_url: window.location.toString()
});
});
......
......@@ -12,11 +12,11 @@ var options = {
// Avoid adding files to this list. Use RequireJS.
libraryFilesToInclude: [
{pattern: 'common/js/vendor/jquery.js', included: true},
{pattern: 'common/js/vendor/jquery-migrate.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/jquery.event.drag-2.2.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/slick.core.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/slick.grid.js', included: true}
{pattern: '../../common/static/common/js/vendor/jquery.js', included: true},
{pattern: '../../common/static/common/js/vendor/jquery-migrate.js', included: true},
{pattern: '../../common/static/js/vendor/jquery.event.drag-2.2.js', included: true},
{pattern: '../../common/static/js/vendor/slick.core.js', included: true},
{pattern: '../../common/static/js/vendor/slick.grid.js', included: true}
],
libraryFiles: [
......@@ -27,6 +27,7 @@ var options = {
// Otherwise Istanbul which is used for coverage tracking will cause tests to not run.
sourceFiles: [
{pattern: 'coffee/src/**/!(*spec).js'},
{pattern: 'discussion/js/**/!(*spec).js'},
{pattern: 'js/**/!(*spec|djangojs).js'},
{pattern: 'lms/js/**/!(*spec).js'},
{pattern: 'support/js/**/!(*spec).js'},
......@@ -34,19 +35,13 @@ var options = {
],
specFiles: [
{pattern: 'js/spec/**/*spec.js'},
{pattern: 'lms/js/spec/**/*spec.js'},
{pattern: 'support/js/spec/**/*spec.js'},
{pattern: 'teams/js/spec/**/*spec.js'},
{pattern: 'xmodule_js/common_static/coffee/spec/**/*.js'}
{pattern: '../**/*spec.js'}
],
fixtureFiles: [
{pattern: 'js/fixtures/**/*.html'},
{pattern: 'lms/fixtures/**/*.html'},
{pattern: 'support/templates/**/*.*'},
{pattern: 'teams/templates/**/*.*'},
{pattern: 'templates/**/*.*'}
{pattern: '../**/fixtures/**/*.html'},
{pattern: '../**/templates/**/*.html'},
{pattern: '../**/*.underscore'}
],
runFiles: [
......
......@@ -19,6 +19,7 @@
*/
modules: getModulesList([
'discussion/js/discussion_board_factory',
'discussion/js/discussion_profile_page_factory',
'js/api_admin/catalog_preview_factory',
'js/courseware/courseware_factory',
'js/discovery/discovery_factory',
......@@ -77,7 +78,7 @@
'logger': 'empty:',
'utility': 'empty:',
'URI': 'empty:',
'DiscussionModuleView': 'empty:',
'common/js/discussion/discussion_module_view': 'empty:',
'modernizr': 'empty',
// Don't bundle UI Toolkit helpers as they are loaded into the "edx" namespace
......
......@@ -8,8 +8,8 @@
paths: {
'gettext': 'xmodule_js/common_static/js/test/i18n',
'codemirror': 'xmodule_js/common_static/js/vendor/CodeMirror/codemirror',
'jquery': 'xmodule_js/common_static/common/js/vendor/jquery',
'jquery-migrate': 'xmodule_js/common_static/common/js/vendor/jquery-migrate',
'jquery': 'common/js/vendor/jquery',
'jquery-migrate': 'common/js/vendor/jquery-migrate',
'jquery.ui': 'xmodule_js/common_static/js/vendor/jquery-ui.min',
'jquery.eventDrag': 'xmodule_js/common_static/js/vendor/jquery.event.drag-2.2',
'jquery.flot': 'xmodule_js/common_static/js/vendor/flot/jquery.flot.min',
......@@ -87,9 +87,6 @@
'js/student_profile/views/learner_profile_view': 'js/student_profile/views/learner_profile_view',
'js/ccx/schedule': 'js/ccx/schedule',
// Discussion classes loaded explicitly until they are converted to use RequireJS
'DiscussionModuleView': 'xmodule_js/common_static/common/js/discussion/discussion_module_view',
'js/bookmarks/collections/bookmarks': 'js/bookmarks/collections/bookmarks',
'js/bookmarks/models/bookmark': 'js/bookmarks/models/bookmark',
'js/bookmarks/views/bookmarks_list_button': 'js/bookmarks/views/bookmarks_list_button',
......@@ -517,7 +514,7 @@
exports: 'Slick'
},
// Discussions
'xmodule_js/common_static/common/js/discussion/utils': {
'common/js/discussion/utils': {
deps: [
'jquery',
'jquery.timeago',
......@@ -536,135 +533,128 @@
});
}
},
'xmodule_js/common_static/common/js/discussion/content': {
'common/js/discussion/content': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'Content'
},
'xmodule_js/common_static/common/js/discussion/discussion': {
'common/js/discussion/discussion': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils',
'common/js/discussion/utils',
'xmodule_js/common_static/common/js/discussion/content'
],
exports: 'Discussion'
},
'xmodule_js/common_static/common/js/discussion/models/discussion_course_settings': {
'common/js/discussion/discussion_course_settings': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'DiscussionCourseSettings'
},
'xmodule_js/common_static/common/js/discussion/models/discussion_user': {
'common/js/discussion/models/discussion_user': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'DiscussionUser'
},
'xmodule_js/common_static/common/js/discussion/views/discussion_content_view': {
'common/js/discussion/views/discussion_content_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'DiscussionContentView'
},
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_edit_view': {
'common/js/discussion/views/discussion_thread_edit_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'DiscussionThreadEditView'
},
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_list_view': {
'common/js/discussion/views/discussion_thread_list_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'DiscussionThreadListView'
},
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_profile_view': {
'common/js/discussion/views/discussion_thread_profile_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'DiscussionThreadProfileView'
},
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_show_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils',
'xmodule_js/common_static/common/js/discussion/views/discussion_content_view'
'common/js/discussion/utils',
'common/js/discussion/views/discussion_content_view'
],
exports: 'DiscussionThreadShowView'
},
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_view': {
'common/js/discussion/views/discussion_thread_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils',
'xmodule_js/common_static/common/js/discussion/views/discussion_content_view'
'common/js/discussion/utils',
'common/js/discussion/views/discussion_content_view'
],
exports: 'DiscussionThreadView'
},
'xmodule_js/common_static/common/js/discussion/views/discussion_topic_menu_view': {
'common/js/discussion/views/discussion_topic_menu_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'DiscussionTopicMenuView'
},
'xmodule_js/common_static/common/js/discussion/views/discussion_user_profile_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
],
exports: 'DiscussionUserProfileView'
},
'xmodule_js/common_static/common/js/discussion/views/new_post_view': {
'common/js/discussion/views/new_post_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'NewPostView'
},
'xmodule_js/common_static/common/js/discussion/views/thread_response_edit_view': {
'common/js/discussion/views/thread_response_edit_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'ThreadResponseEditView'
},
'xmodule_js/common_static/common/js/discussion/views/thread_response_show_view': {
'common/js/discussion/views/thread_response_show_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'ThreadResponseShowView'
},
'xmodule_js/common_static/common/js/discussion/views/thread_response_view': {
'common/js/discussion/views/thread_response_view': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'ThreadResponseView'
},
'DiscussionModuleView': {
'common/js/discussion/discussion_module_view': {
deps: [
'jquery',
'underscore',
'backbone',
'gettext',
'URI',
'xmodule_js/common_static/common/js/discussion/content',
'xmodule_js/common_static/common/js/discussion/discussion',
'xmodule_js/common_static/common/js/discussion/utils',
'xmodule_js/common_static/common/js/discussion/models/discussion_course_settings',
'xmodule_js/common_static/common/js/discussion/models/discussion_user',
'xmodule_js/common_static/common/js/discussion/views/discussion_content_view',
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_edit_view',
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_list_view',
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_profile_view',
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_show_view',
'xmodule_js/common_static/common/js/discussion/views/discussion_thread_view',
'xmodule_js/common_static/common/js/discussion/views/discussion_topic_menu_view',
'xmodule_js/common_static/common/js/discussion/views/discussion_user_profile_view',
'xmodule_js/common_static/common/js/discussion/views/new_post_view',
'xmodule_js/common_static/common/js/discussion/views/thread_response_edit_view',
'xmodule_js/common_static/common/js/discussion/views/thread_response_show_view',
'xmodule_js/common_static/common/js/discussion/views/thread_response_view'
'common/js/discussion/content',
'common/js/discussion/discussion',
'common/js/discussion/models/discussion_course_settings',
'common/js/discussion/models/discussion_user',
'common/js/discussion/utils',
'common/js/discussion/views/discussion_content_view',
'common/js/discussion/views/discussion_thread_edit_view',
'common/js/discussion/views/discussion_thread_list_view',
'common/js/discussion/views/discussion_thread_profile_view',
'common/js/discussion/views/discussion_thread_show_view',
'common/js/discussion/views/discussion_thread_view',
'common/js/discussion/views/discussion_topic_menu_view',
'common/js/discussion/views/new_post_view',
'common/js/discussion/views/thread_response_edit_view',
'common/js/discussion/views/thread_response_show_view',
'common/js/discussion/views/thread_response_view'
],
exports: 'DiscussionModuleView'
},
'xmodule_js/common_static/common/js/spec_helpers/discussion_spec_helper': {
'common/js/spec_helpers/discussion_spec_helper': {
deps: [
'xmodule_js/common_static/common/js/discussion/utils'
'common/js/discussion/utils'
],
exports: 'DiscussionSpecHelper'
}
......@@ -672,6 +662,9 @@
});
var testFiles = [
'discussion/js/spec/discussion_board_factory_spec.js',
'discussion/js/spec/discussion_profile_page_factory_spec.js',
'discussion/js/spec/views/discussion_user_profile_view_spec.js',
'lms/js/spec/preview/preview_factory_spec.js',
'js/spec/api_admin/catalog_preview_spec.js',
'js/spec/courseware/bookmark_button_view_spec.js',
......
......@@ -21,8 +21,8 @@ $static-path: '../..' !default;
@import '../shared-v2/help-tab';
// Compatibility support for non-Pattern Library mixins and extensions
@import 'utilities/v1-compatibility';
@import 'utilities/variables-v2';
@import 'utilities/v1-compatibility';
// Discussion styling
@import 'mixins';
......
......@@ -10,7 +10,7 @@ body.discussion {
width: 100%;
h1 {
font-size: 20px;
font-size: $forum-x-large-font-size;
}
.form-row {
......@@ -19,13 +19,13 @@ body.discussion {
.post-cancel {
@include white-button;
float: left;
@include float(left);
margin: ($baseline/2) 0 0 ($baseline*0.75);
}
.post-update {
@include blue-button;
float: left;
@include float(left);
margin-top: ($baseline/2);
padding-bottom: ($baseline/10);
height: 37px;
......@@ -38,19 +38,19 @@ body.discussion {
.edit-post-title {
box-sizing: border-box;
border: 1px solid $forum-color-border;
border-radius: 3px;
border-radius: $forum-border-radius;
padding: 0 ($baseline/2);
width: 100%;
height: 40px;
box-shadow: 0 1px 3px $shadow-l1 inset;
color: $dark-gray;
font-size: 16px;
font-size: $forum-large-font-size;
font-family: $sans-serif;
}
}
.comments .edit-post-form h1 {
@extend %t-title6;
font-size: $forum-large-font-size;
}
.thread-title {
......@@ -58,7 +58,7 @@ body.discussion {
margin-bottom: $baseline;
color: $gray-d3;
font-weight: 700;
font-size: 21px;
font-size: $forum-x-large-font-size;
}
.wmd-panel {
......@@ -71,13 +71,13 @@ body.discussion {
}
.wmd-input {
@include border-radius(3px, 3px, 0, 0);
@include border-radius($forum-border-radius, $forum-border-radius, 0, 0);
border: 1px solid $forum-color-border;
width: 100%;
height: 150px;
background-color: $gray-l4;
font-style: normal;
font-size: 0.8em;
font-size: $forum-base-font-size;
font-family: $f-sans-serif;
line-height: 1.6em;
......@@ -149,22 +149,22 @@ body.discussion {
padding: $baseline;
> div {
font-size: 0.8em;
font-size: $forum-base-font-size;
font-family: $sans-serif;
}
b {
font-size: 16px;
font-size: $forum-large-font-size;
}
> form > input[type="text"] {
border-radius: 3px;
border-radius: $forum-border-radius;
color: $gray-d3;
}
> form > input[type="button"] {
border: 1px solid #888;
font-size: 14px;
font-size: $forum-base-font-size;
font-family: $sans-serif;
}
......@@ -184,7 +184,7 @@ body.discussion {
.bottom-post-status {
padding: 30px;
font-size: 20px;
font-size: $forum-x-large-font-size;
font-weight: 700;
color: $gray-l3;
text-align: center;
......@@ -192,7 +192,6 @@ body.discussion {
.discussion-article {
position: relative;
min-height: 500px;
a {
word-wrap: break-word;
......@@ -210,9 +209,9 @@ body.discussion {
blockquote {
background: $gray-l5;
border-radius: 3px;
border-radius: $forum-border-radius;
padding: ($baseline/4) ($baseline/2);
font-size: 14px;
font-size: $forum-base-font-size;
}
.comment-form {
......@@ -237,7 +236,7 @@ body.discussion {
padding: 0 ($baseline/2);
box-sizing: border-box;
border: 1px solid $forum-color-border;
border-radius: 3px;
border-radius: $forum-border-radius;
box-shadow: 0 1px 3px $shadow-l1 inset;
@include transition(border-color .1s linear 0s);
......@@ -257,7 +256,7 @@ body.discussion {
padding: 0 ($baseline/2);
h4 {
font-size: 16px;
font-size: $forum-large-font-size;
font-weight: 700;
}
}
......@@ -298,7 +297,7 @@ body.discussion {
// ====================
// inline discussion module and profile thread styling
// inline discussion module
.discussion-module {
@extend .discussion-body;
......@@ -306,11 +305,11 @@ body.discussion {
margin: $baseline 0;
padding: $baseline;
background: #f6f6f6 !important;
border-radius: 3px;
border-radius: $forum-border-radius;
header {
.anonymous{
font-size: 15px;
font-size: $forum-base-font-size;
}
}
......@@ -328,129 +327,94 @@ body.discussion {
.loading-animation {
background-image: url('#{$static-path}/images/spinner.gif');
}
}
.discussion-show {
position: relative;
top: 3px;
font-size: 14px;
text-align: center;
&.shown {
.show-hide-discussion-icon {
background-position: 0 0;
}
}
.discussion-show {
position: relative;
top: 3px;
font-size: $forum-base-font-size;
text-align: center;
&.shown {
.show-hide-discussion-icon {
display: inline-block;
position: relative;
top: 5px;
@include margin-right(6px);
width: 21px;
height: 19px;
background: url('#{$static-path}/images/show-hide-discussion-icon.png') no-repeat;
background-position: -21px 0;
background-position: 0 0;
}
}
section.discussion {
clear: both;
padding-top: $baseline;
.threads {
}
.show-hide-discussion-icon {
display: inline-block;
position: relative;
top: 5px;
@include margin-right(6px);
width: 21px;
height: 19px;
background: url('#{$static-path}/images/show-hide-discussion-icon.png') no-repeat;
background-position: -21px 0;
}
}
.new-post-article {
display: none;
margin-top: $baseline;
section.discussion {
@include clearfix();
}
.inner-wrapper {
max-width: 1180px;
min-width: 760px;
margin: auto;
}
.new-post-article {
display: none;
margin-top: $baseline;
.thread-title {
display: block;
margin-bottom: $baseline;
font-size: 21px;
color: $gray-d3;
font-weight: 700;
}
.inner-wrapper {
max-width: 1180px;
min-width: 760px;
margin: auto;
}
section.discussion-pagination {
margin-top: ($baseline*1.5);
nav.discussion-paginator {
float: right;
ol {
li {
list-style: none;
display: inline-block;
padding-right: 0.5em;
a {
@include white-button;
}
&.current-page span {
display: inline-block;
height: 35px;
padding: 0 ($baseline*0.75);
border: 1px solid $forum-color-border;
border-radius: 3px;
font-size: 13px;
font-weight: 700;
line-height: 32px;
color: $gray-d3;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
}
}
}
}
.thread-title {
display: block;
margin-bottom: $baseline;
font-size: $forum-x-large-font-size;
color: $gray-d3;
font-weight: 700;
}
}
.edit-post-form {
width: 100%;
margin-bottom: $baseline;
@include clearfix();
box-sizing: border-box;
.edit-post-form {
@include clearfix();
width: 100%;
margin-bottom: $baseline;
box-sizing: border-box;
.form-row {
margin-top: $baseline;
}
.form-row {
margin-top: $baseline;
}
.post-cancel {
@include white-button;
float: left;
margin: ($baseline/2) 0 0 ($baseline*0.75);
}
.post-cancel {
@include white-button;
@include float(left);
@include margin($baseline/2, 0, 0, $baseline*0.75);
}
.post-update {
@include blue-button;
float: left;
height: 37px;
margin-top: ($baseline/2);
padding-bottom: 2px;
.post-update {
@include blue-button;
@include float(left);
height: 37px;
margin-top: ($baseline/2);
padding-bottom: 2px;
&:hover, &:focus {
border-color: #222;
}
&:hover, &:focus {
border-color: #222;
}
}
.edit-post-title {
width: 100%;
height: 40px;
padding: 0 ($baseline/2);
box-sizing: border-box;
border-radius: 3px;
border: 1px solid $forum-color-border;
font-size: 16px;
font-family: $sans-serif;
color: $gray-d3;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15) inset;
}
.edit-post-title {
width: 100%;
height: 40px;
padding: 0 ($baseline/2);
box-sizing: border-box;
border-radius: $forum-border-radius;
border: 1px solid $forum-color-border;
font-size: $forum-large-font-size;
font-family: $sans-serif;
color: $gray-d3;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15) inset;
}
}
......@@ -461,6 +425,37 @@ body.discussion {
// ====================
// post pagination
section.discussion-pagination {
margin-top: ($baseline*1.5);
nav.discussion-paginator {
@include float(right);
ol {
li {
list-style: none;
display: inline-block;
padding-right: 0.5em;
a {
@include white-button;
}
&.current-page span {
display: inline-block;
height: 35px;
padding: 0 ($baseline*0.75);
border: 1px solid $forum-color-border;
border-radius: $forum-border-radius;
font-size: $forum-base-font-size;
font-weight: 700;
line-height: 32px;
color: $gray-d3;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
}
}
}
}
}
.response-count {
@include float(right);
}
......@@ -477,7 +472,7 @@ body.discussion {
display: block;
padding: ($baseline/2) 0;
color: $gray;
font-size: 14px;
font-size: $forum-base-font-size;
}
.load-response-button {
......
// Layouts for discussion pages
section.user-profile {
.user-profile {
background-color: $sidebar-color;
.user-profile {
padding: 32px 36px;
padding: $baseline;
min-height: 500px;
}
.sidebar-username {
font-weight: 700;
font-size: 18px;
font-size: $forum-large-font-size;
}
.sidebar-user-roles {
margin-top: 6px;
margin-top: $baseline/2;
font-style: italic;
font-size: 13px;
font-size: $forum-base-font-size;
}
.sidebar-threads-count {
margin-top: 14px;
margin-top: $baseline/2;
}
.sidebar-threads-count span,
......@@ -30,11 +30,5 @@ section.user-profile {
}
.discussion-column {
@include float(right);
box-sizing: border-box;
padding-left: ($baseline/2);
width: 68%;
max-width: 800px;
min-height: 500px;
border-radius: 3px;
}
......@@ -4,10 +4,11 @@
@mixin discussion-button() {
display: block;
border: 1px solid;
border-radius: 3px;
border-radius: $forum-border-radius;
height: 35px;
color: $white;
line-height: 35px;
font-size: 13px;
font-size: $forum-base-font-size;
white-space: nowrap; // Prevent word-break in Arabic in Google Chrome
text-shadow: none;
padding: 0 ($baseline*0.75);
......@@ -52,20 +53,20 @@
box-sizing: border-box;
margin-top: 0;
border: 1px solid $forum-color-border;
border-radius: 3px 3px 0 0;
border-radius: $forum-border-radius $forum-border-radius 0 0;
padding: ($baseline/2);
width: 100%;
height: 125px;
background: $forum-color-background;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15) inset;
font-size: 13px;
font-size: $forum-base-font-size;
font-family: $sans-serif;
line-height: 1.6;
}
@mixin discussion-wmd-preview-container {
box-sizing: border-box;
@include border-radius(0, 0, 3px, 3px);
@include border-radius(0, 0, $forum-border-radius, $forum-border-radius);
border: 1px solid $gray-l1;
border-top: none;
width: 100%;
......@@ -84,7 +85,7 @@
padding-top: 3px;
width: 100%;
color: $gray-l2;
font-size: 11px;
font-size: $forum-small-font-size;
}
@mixin discussion-wmd-preview {
......@@ -113,11 +114,11 @@
@mixin forum-post-label($color) {
@extend %t-weight4;
@include font-size(9);
font-size: $forum-small-font-size;
display: inline;
margin-top: ($baseline/4);
border: 1px solid;
border-radius: 3px;
border-radius: $forum-border-radius;
padding: 1px 6px;
white-space: nowrap;
......@@ -138,11 +139,11 @@
}
@mixin forum-user-label($color) {
@include font-size(9);
@include margin-left($baseline/4);
@extend %t-weight5;
font-size: $forum-small-font-size;
vertical-align: middle;
margin-left: ($baseline/4);
border-radius: 2px;
border-radius: $forum-border-radius;
padding: 0 ($baseline/5);
background: $color;
font-style: normal;
......
......@@ -70,7 +70,7 @@
box-shadow: 0 1px 1px $shadow-l1;
position: relative;
width: 100%;
border-radius: 3px;
border-radius: $forum-border-radius;
margin: ($baseline/4) 0 0 0;
border: 1px solid $gray-l3;
padding: ($baseline/2) ($baseline*0.75);
......@@ -121,16 +121,16 @@
box-sizing: border-box;
display: inline-block;
border: 1px solid transparent;
border-radius: ($baseline/4);
border-radius: $forum-border-radius;
color: $gray-l1;
.action-icon {
@extend %t-icon7;
display: inline-block;
font-size: $forum-small-font-size;
height: $baseline;
width: $baseline;
border: 1px solid $gray-l3;
border-radius: 3px;
border: 1px solid $forum-color-border;
border-radius: $forum-border-radius;
text-align: center;
color: $gray-l1;
......@@ -156,7 +156,7 @@
}
.action-icon {
border-radius: 0 3px 3px 0;
border-radius: 0 $forum-border-radius $forum-border-radius 0;
}
}
......@@ -287,9 +287,9 @@
}
.action-icon {
@include margin-left($baseline/4);
display: inline-block;
width: ($baseline/2);
margin-left: ($baseline/4);
color: inherit;
}
......
......@@ -54,9 +54,9 @@
.wmd-input {
width: 100%;
height: 150px;
border-radius: 3px 3px 0 0;
border-radius: $forum-border-radius $forum-border-radius 0 0;
font-style: normal;
font-size: 0.8em;
font-size: $forum-base-font-size;
font-family: Monaco, 'Lucida Console', monospace;
line-height: 1.6em;
......@@ -75,9 +75,9 @@
}
.wmd-spacer {
@include margin-left(14px);
position: absolute;
display: inline-block;
margin-left: 14px;
width: 1px;
height: 20px;
background-color: Silver;
......@@ -129,23 +129,23 @@
padding: $baseline;
> div {
font-size: 0.8em;
font-size: $forum-base-font-size;
font-family: arial, helvetica, sans-serif;
}
b {
font-size: 16px;
font-size: $forum-large-font-size;
}
> form > input[type="text"] {
border-radius: 3px;
border-radius: $forum-border-radius;
color: #333;
}
> form > input[type="button"] {
border: 1px solid #888;
font-family: $sans-serif;
font-size: 14px;
font-size: $forum-x-large-font-size;
}
> form > input[type="file"] {
......
......@@ -2,12 +2,8 @@
// ====================
.forum-nav {
box-sizing: border-box;
@include float(left);
position: relative;
width: 31%;
border: 1px solid #aaa;
border-radius: 3px;
border-radius: $forum-border-radius;
}
// ------
......@@ -45,13 +41,13 @@
}
.icon {
@include font-size(14);
@include margin-right($baseline/4);
font-size: $forum-base-font-size;
}
}
.forum-nav-browse-current {
@include font-size(12);
font-size: $forum-small-font-size;
}
.forum-nav-browse-drop-arrow {
......@@ -69,7 +65,7 @@
}
.forum-nav-search .icon {
@include font-size(12);
font-size: $forum-small-font-size;
position: absolute;
margin-top: -6px;
top: 50%;
......@@ -95,7 +91,7 @@
}
.forum-nav-browse-filter .icon {
@include font-size(12);
font-size: $forum-small-font-size;
position: absolute;
margin-top: -6px;
top: 50%;
......@@ -134,7 +130,7 @@
}
.forum-nav-browse-menu {
@include font-size(14);
font-size: $forum-base-font-size;
overflow-y: scroll;
list-style: none;
}
......@@ -156,7 +152,7 @@
// -------------------
.forum-nav-refine-bar {
@include clearfix();
@include font-size(11);
font-size: $forum-small-font-size;
border-bottom: 1px solid $forum-color-border;
background-color: $gray-l5;
padding: ($baseline/4) ($baseline/2);
......@@ -284,7 +280,7 @@
width: 7%;
.icon {
@include font-size(14);
font-size: $forum-base-font-size;
&:before {
......@@ -319,19 +315,19 @@
}
.forum-nav-thread-title {
@extend %t-title7;
font-size: $forum-base-font-size;
display: block;
}
%forum-nav-thread-wrapper-2-content {
@include font-size(11);
@include margin-right($baseline/4);
font-size: $forum-small-font-size;
display: inline-block;
margin-right: ($baseline/4);
text-align: center;
color: $black;
&:last-child {
margin-right: 0;
@include margin-right(0);
}
}
......@@ -345,7 +341,7 @@
position: relative;
@include margin-left($baseline/4);
margin-bottom: ($baseline/4); // Because tail is position: absolute
border-radius: 2px;
border-radius: $forum-border-radius;
padding: ($baseline/10) ($baseline/5);
min-width: 2em; // Fit most comment counts but allow expansion if necessary
background-color: $gray-l4;
......
......@@ -44,7 +44,7 @@ body.discussion {
// alert copy
.message {
@include font-size(12);
font-size: $forum-small-font-size;
color: $white;
em {
......@@ -66,11 +66,11 @@ body.discussion {
text-align: right;
.control {
@include font-size(14);
@include transition(none);
@extend %t-weight5;
padding: ($baseline/4) ($baseline/2);
color: $white;
font-size: $forum-base-font-size;
// reseting poorly globally scoped hover/focus state for this control
&:hover, &:focus {
......
......@@ -14,13 +14,13 @@
// Override global input rules
.forum-nav-search-input {
@include padding-left($baseline/4 !important);
@include padding-right($baseline/2 + 12px !important); // Leave room for icon
box-shadow: none !important;
border: 1px solid $forum-color-border !important;
border-radius: 3px !important;
border-radius: $forum-border-radius !important;
height: auto !important;
@include padding-left($baseline/4 !important);
@include padding-right($baseline/2 + 12px !important); // Leave room for icon
font-size: 12px !important;
font-size: $forum-small-font-size !important;
}
// Firefox does not compute the correct containing box for absolute positioning
......@@ -45,12 +45,12 @@
// Override global input rules
.forum-nav-browse-filter-input {
@include padding-left($baseline/4);
@include padding-right($baseline/2 + 12px); // Leave room for icon
box-shadow: none !important;
border-radius: 3px !important;
border-radius: $forum-border-radius !important;
height: auto !important;
padding-left: ($baseline/4) !important;
padding-right: ($baseline/2 + 12px) !important; // Leave room for icon
font-size: 12px !important;
font-size: $forum-small-font-size !important;
}
// Override global ul rules
......
......@@ -31,46 +31,6 @@
%ui-depth4 { z-index: 10000; }
%ui-depth5 { z-index: 100000; }
%t-icon1 {
@include font-size(48);
}
%t-icon2 {
@include font-size(36);
}
%t-icon3 {
@include font-size(24);
}
%t-icon4 {
@include font-size(18);
}
%t-icon5 {
@include font-size(16);
}
%t-icon6 {
@include font-size(14);
}
%t-icon7 {
@include font-size(12);
}
%t-icon8 {
@include font-size(11);
}
%t-icon9 {
@include font-size(10);
}
%t-icon-solo {
@include line-height(0);
}
// weights
%t-ultrastrong {
font-weight: 700;
......@@ -91,97 +51,21 @@
font-weight: 200;
}
// headings/titles
%t-title {
font-family: $f-sans-serif;
}
%t-title1 {
@extend %t-title;
@include font-size(60);
@include line-height(60);
}
%t-title2 {
@extend %t-title;
@include font-size(48);
@include line-height(48);
}
%t-title3 {
@include font-size(36);
@include line-height(36);
}
%t-title4 {
@extend %t-title;
@include font-size(24);
@include line-height(24);
}
%t-title5 {
@extend %t-title;
@include font-size(18);
@include line-height(18);
}
%t-title6 {
@extend %t-title;
@include font-size(16);
@include line-height(16);
}
%t-title7 {
@extend %t-title;
@include font-size(14);
@include line-height(14);
}
%t-title8 {
@extend %t-title;
@include font-size(12);
@include line-height(12);
}
%t-title9 {
@extend %t-title;
@include font-size(11);
@include line-height(11);
}
// copy
%t-copy {
font-family: $f-sans-serif;
}
%t-copy-base {
@extend %t-copy;
@include font-size(16);
@include line-height(16);
}
%t-copy-lead1 {
@extend %t-copy;
@include font-size(18);
@include line-height(18);
}
%t-copy-lead2 {
@extend %t-copy;
@include font-size(24);
@include line-height(24);
}
%t-copy-sub1 {
@extend %t-copy;
@include font-size(14);
@include line-height(14);
font-size: $forum-base-font-size;
}
%t-copy-sub2 {
@extend %t-copy;
@include font-size(12);
@include line-height(12);
font-size: $forum-small-font-size;
}
// extends - UI - removes list styling/spacing when using uls, ols for navigation and less content-centric cases
......
......@@ -21,3 +21,12 @@ $forum-color-reading-thread: $gray-d3 !default;
$post-image-dimension: ($baseline*3) !default; // image size + margin
$response-image-dimension: ($baseline*2.5) !default; // image size + margin
$comment-image-dimension: ($baseline*2) !default; // image size + margin
// font sizes
$forum-base-font-size: 14px;
$forum-x-large-font-size: 21px;
$forum-large-font-size: 16px;
$forum-small-font-size: 12px;
// borders
$forum-border-radius: 3px;
......@@ -21,3 +21,12 @@ $forum-color-reading-thread: palette(grayscale, dark) !default;
$post-image-dimension: ($baseline*3) !default; // image size + margin
$response-image-dimension: ($baseline*2.5) !default; // image size + margin
$comment-image-dimension: ($baseline*2) !default; // image size + margin
// font sizes
$forum-base-font-size: font-size(small);
$forum-x-large-font-size: font-size(x-large);
$forum-large-font-size: font-size(base);
$forum-small-font-size: font-size(x-small);
// borders
$forum-border-radius: $component-border-radius;
......@@ -7,7 +7,7 @@
@include clearfix();
box-sizing: border-box;
margin: 0;
border-radius: 3px;
border-radius: $forum-border-radius;
padding: $baseline;
max-width: 1180px;
......@@ -32,7 +32,7 @@
display: inline-block;
width: 25%;
vertical-align: top;
font-size: 12px;
font-size: $forum-small-font-size;
line-height: 40px;
}
......@@ -47,7 +47,8 @@
display: inline-block;
@include padding-left($baseline);
width: 50%;
font-size: 12px;
font-size: $forum-small-font-size;
}
}
......@@ -73,11 +74,11 @@
padding: 0 $baseline 0 ($baseline*0.75);
width: 100%;
height: 40px;
font-size: 14px;
font-size: $forum-base-font-size;
line-height: 36px;
.drop-arrow {
float: right;
@include float(right);
color: #999;
}
}
......@@ -89,7 +90,7 @@
.post-type-label {
@extend %cont-truncated;
@include white-button;
@include font-size(14);
font-size: $forum-base-font-size;
box-sizing: border-box;
display: inline-block;
padding: 0 ($baseline/2);
......@@ -101,7 +102,7 @@
line-height: 36px;
.icon {
margin-right: ($baseline/4);
@include margin-right($baseline/4);
}
}
......@@ -119,13 +120,13 @@
input[type=text].field-input {
box-sizing: border-box;
border: 1px solid $forum-color-border;
border-radius: 3px;
border-radius: $forum-border-radius;
padding: 0 $baseline/2;
height: 40px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15) inset;
color: #333;
font-weight: 700;
font-size: 16px;
font-size: $forum-large-font-size;
font-family: 'Open Sans', sans-serif;
}
......@@ -134,7 +135,7 @@
display: inline-block;
@include margin-right($baseline);
border: 1px solid transparent;
border-radius: 3px;
border-radius: $forum-border-radius;
padding: ($baseline/2);
&:hover {
......@@ -147,11 +148,11 @@
}
.post-option-input {
margin-right: ($baseline/2);
@include margin-right($baseline/2);
}
.icon {
margin-right: 0.5em;
@include margin-right($baseline/2);
}
}
}
......@@ -162,8 +163,8 @@
.forum-new-post-form {
.submit {
@include blue-button;
@include margin-right($baseline/2);
display: inline-block;
margin-right: ($baseline/2);
}
.cancel {
......@@ -179,7 +180,7 @@
.edit-post-form {
.post-errors {
margin-bottom: $baseline;
border-radius: 3px;
border-radius: $forum-border-radius;
padding: 0;
background: $error-color;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) inset, 0 1px 0 rgba(255, 255, 255, .2);
......@@ -230,7 +231,7 @@
width: 100%;
height: 30px;
color: #333;
font-size: 11px;
font-size: $forum-small-font-size;
line-height: 16px;
}
......@@ -249,7 +250,7 @@
.topic-title {
display: block;
padding: ($baseline/4) ($baseline/2);
font-size: 14px;
font-size: $forum-base-font-size;
}
a.topic-title {
......
......@@ -24,7 +24,7 @@
}
.home-title {
@extend %t-title5;
font-size: $forum-large-font-size;
color: $black;
margin-bottom: ($baseline/4);
}
......@@ -58,7 +58,7 @@
vertical-align: middle;
.count {
@extend %t-title4;
font-size: $forum-x-large-font-size;
display: inline-block;
padding: 0 ($baseline/2);
vertical-align: middle;
......@@ -73,7 +73,7 @@
.home-helpgrid {
border-bottom: none;
border-radius: 3px;
border-radius: $forum-border-radius;
border: 1px solid $forum-color-border;
box-shadow: 0 1px 3px rgba(0, 0, 0, .15);
}
......@@ -84,11 +84,11 @@
.row-title {
padding: ($baseline*1.5) $baseline;
background-color: #dedede;
font-size: 12px;
font-size: $forum-small-font-size;
}
.row-item-full, .row-item {
font-size: 12px;
font-size: $forum-small-font-size;
padding: 0 ($baseline/2);
width: 26%;
vertical-align: middle;
......@@ -118,7 +118,7 @@
@include margin-right($baseline/2);
display: inline-block;
@include padding($baseline/4, 0, $baseline/2, 0);
border-radius: 5px;
border-radius: $forum-border-radius;
border: 1px solid gray;
.email-setting {
......
......@@ -24,14 +24,14 @@
position: relative;
margin: $baseline 0;
border: 1px solid $forum-color-border;
border-radius: 3px;
border-radius: $forum-border-radius;
box-shadow: 0 0 1px $shadow;
}
// wrapper - main response area
.discussion-response {
box-sizing: border-box;
@include border-radius(3px, 3px, 0, 0);
@include border-radius($forum-border-radius, $forum-border-radius, 0, 0);
padding: $baseline;
background-color: $forum-color-background;
}
......@@ -48,8 +48,8 @@
// CASE: larger username for responses
.username {
@include font-size(14);
@extend %t-weight5;
font-size: $forum-base-font-size;
}
}
......@@ -68,7 +68,7 @@
// +CASE: answered question - collapsed comments in answers
.forum-response .action-show-comments {
@include font-size(13);
font-size: $forum-base-font-size;
box-sizing: border-box;
display: block;
padding: ($baseline/2) $baseline;
......@@ -95,6 +95,7 @@
// CASE: banner - staff response
.staff-banner {
@include border-radius($forum-border-radius, $forum-border-radius, 0, 0);
position: absolute;
top: 0;
left: 0;
......@@ -102,9 +103,8 @@
height: 14px;
padding: 1px ($baseline/4);
box-sizing: border-box;
border-radius: 2px 2px 0 0;
background: #009fe2;
font-size: 9px;
font-size: $forum-small-font-size;
font-weight: 700;
color: $white;
}
......@@ -118,9 +118,9 @@
height: 14px;
padding: 1px ($baseline/4);
box-sizing: border-box;
border-radius: 2px 2px 0 0;
border-radius: $forum-border-radius $forum-border-radius 0 0;
background: $forum-color-community-ta;
font-size: 9px;
font-size: $forum-small-font-size;
font-weight: 700;
color: $white;
}
......@@ -138,7 +138,7 @@
// +comments styling
.container .discussion-body .comments {
@extend %ui-no-list;
border-radius: 0 0 3px 3px;
border-radius: 0 0 $forum-border-radius $forum-border-radius;
background: $gray-l6;
box-shadow: 0 1px 3px -1px $shadow inset;
......@@ -149,9 +149,9 @@
blockquote {
background: $gray-l4;
border-radius: 3px;
border-radius: $forum-border-radius;
padding: ($baseline/4) ($baseline/2);
font-size: 14px;
font-size: $forum-base-font-size;
}
.comment-form {
......@@ -160,12 +160,12 @@
.comment-form-input {
padding: ($baseline/4) ($baseline/2);
background-color: $forum-color-background;
font-size: 14px;
font-size: $forum-base-font-size;
}
.discussion-submit-comment {
@include blue-button;
float: left;
@include float(left);
margin-top: 8px;
}
......
......@@ -127,7 +127,7 @@ body.discussion {
}
img {
border-radius: 3px;
border-radius: $forum-border-radius;
}
}
}
......@@ -138,21 +138,17 @@ body.discussion {
}
// +post - individual element styling
body.discussion .discussion-post,
body.discussion .discussion-article,
body.view-in-course .discussion-post,
body.view-in-course .discussion-article {
// NOTE: discussion-article is used for inline discussion modules.
// NOTE: discussion-article is used for inline discussion modules.
.discussion-post,
.discussion-article {
@include clearfix();
.post-header-content {
// post title
.post-title {
@extend %t-title4;
@extend %t-ultrastrong;
font-size: $forum-x-large-font-size;
margin-bottom: ($baseline/4);
letter-spacing: 0;
}
}
......@@ -211,14 +207,14 @@ body.view-in-course .discussion-article {
.discussion-article {
@include transition(all .2s linear 0s);
border: 1px solid $forum-color-border;
border-radius: 3px;
border-radius: $forum-border-radius;
min-height: 0;
background: $forum-color-background;
box-shadow: 0 1px 0 $shadow;
@include transition(all .2s linear 0s);
.thread-wrapper {
@include border-radius(3px, 3px, 0, 0);
@include border-radius($forum-border-radius, $forum-border-radius, 0, 0);
position: relative;
overflow-x: hidden;
overflow-y: auto;
......@@ -228,16 +224,16 @@ body.view-in-course .discussion-article {
.discussion-post {
.inline-comment-count {
@include margin-right($baseline/2);
@extend %ui-depth2;
@include float(right);
position: relative;
float: right;
display: block;
height: 27px;
margin-top: 6px;
margin-right: 8px;
padding: 0 8px;
border-radius: ($baseline/4);
font-size: 12px;
border-radius: $forum-border-radius;
font-size: $forum-small-font-size;
font-weight: 400;
line-height: 25px;
color: #888;
......@@ -250,15 +246,15 @@ body.view-in-course .discussion-article {
margin-bottom: ($baseline*0.75);
.posted-by {
float: left;
margin-right: ($baseline/4);
font-size: 16px;
@include margin-right($baseline/4);
@include float(left);
font-size: $forum-large-font-size;
}
}
.response-body {
margin-bottom: 0.2em;
font-size: 14px;
font-size: $forum-base-font-size;
}
}
......@@ -289,12 +285,12 @@ body.view-in-course .discussion-article {
a {
display: block;
padding: ($baseline*0.25) $baseline;
font-size: 12px;
font-size: $forum-small-font-size;
line-height: 30px;
.icon {
@include margin-right($baseline*0.25);
color: $link-color;
margin-right: ($baseline*0.25);
}
}
}
......
......@@ -11,12 +11,12 @@
@include clearfix();
border: 1px solid $lms-border-color;
background-color: $lms-container-background-color;
padding: $baseline;
}
.page-header {
@include clearfix();
border-bottom: 1px solid $lms-border-color;
padding: $baseline;
.page-title {
@extend %t-title4;
......@@ -67,6 +67,6 @@
}
.page-content {
padding-top: $baseline;
padding: $baseline;
}
}
......@@ -52,7 +52,6 @@ ${static.get_page_title_breadcrumbs(course_name())}
<script type="text/javascript" src="${static.url('js/vendor/codemirror-compressed.js')}"></script>
<%static:js group='courseware'/>
<%static:js group='discussion'/>
<%include file="../discussion/_js_body_dependencies.html" />
% if staff_access:
......
......@@ -73,7 +73,8 @@ ${static.get_page_title_breadcrumbs(course_name())}
<script type="text/javascript" src="${static.url('js/vendor/codemirror-compressed.js')}"></script>
<%static:js group='courseware'/>
<%static:js group='discussion'/>
<%include file="../discussion/_js_body_dependencies.html" />
% if settings.FEATURES.get('ENABLE_COURSEWARE_SEARCH'):
<%static:require_module module_name="js/search/course/course_search_factory" class_name="CourseSearchFactory">
var courseId = $('.courseware-results').data('courseId');
......@@ -85,7 +86,6 @@ ${static.get_page_title_breadcrumbs(course_name())}
CoursewareFactory();
</%static:require_module>
<%include file="../discussion/_js_body_dependencies.html" />
% if staff_access:
<%include file="xqa_interface.html"/>
% endif
......
<%page expression_filter="h"/>
<%inherit file="../courseware/course_navigation.html" />
<%!
from django.utils.translation import ugettext as _
from django_comment_client.permissions import has_permission
%>
<%block name="extratabs">
% if has_permission(user, 'create_thread', course.id):
<li class="right">
<button class="new-post-btn btn btn-small">${_("Add a Post")}</button>
</li>
% endif
</%block>
<%page args="disable_fast_preview=True"/>
## mako
<%namespace name='static' file='/static_content.html'/>
<%page args="disable_fast_preview=True" expression_filter="h"/>
<%!
from openedx.core.djangolib.js_utils import js_escaped_string
%>
<%include file="/mathjax_include.html" args="disable_fast_preview=disable_fast_preview"/>
<%static:js group='discussion'/>
## Add RequireJS definitions for each discussion class
<%
discussion_classes = [
['Discussion', 'common/js/discussion/discussion'],
['DiscussionModuleView', 'common/js/discussion/discussion_module_view'],
['DiscussionThreadView', 'common/js/discussion/views/discussion_thread_view'],
['DiscussionThreadListView', 'common/js/discussion/views/discussion_thread_list_view'],
['DiscussionThreadProfileView', 'common/js/discussion/views/discussion_thread_profile_view'],
['DiscussionUtil', 'common/js/discussion/utils'],
['NewPostView', 'common/js/discussion/views/new_post_view'],
]
%>
<script type="text/javascript">
% for discussion_class in discussion_classes:
RequireJS.define(
'${discussion_class[1] | n, js_escaped_string}',
[],
function() {
return window['${discussion_class[0] | n, js_escaped_string}'];
}
);
% endfor
</script>
......@@ -16,7 +16,7 @@ template_names = [
'thread', 'thread-show', 'thread-edit', 'thread-response', 'thread-response-show', 'thread-response-edit',
'response-comment-show', 'response-comment-edit', 'thread-list-item', 'discussion-home', 'search-alert',
'new-post', 'thread-type', 'new-post-menu-entry', 'new-post-menu-category', 'topic', 'post-user-display',
'inline-discussion', 'pagination', 'user-profile', 'profile-thread', 'customwmd-prompt', 'nav-loading'
'inline-discussion', 'pagination', 'profile-thread', 'customwmd-prompt', 'nav-loading'
]
## same, but without trailing "-template" in script ID - these templates does not contain any free variables
......
......@@ -64,8 +64,30 @@
</div>
</header>
<div class="page-content">
<h3>This is where the page content belongs</h3>
<p>Useful stuff goes here</p>
<div class="layout layout-1t2t">
<aside class="layout-col layout-col-a" role="complementary" aria-label="Navigation">
<h3>Sidebar</h3>
<ul>
<li>Item one</li>
<li>Item two</li>
<li>Item three</li>
</ul>
</aside>
<main id="main" aria-label="Content" tabindex="-1" class="layout-col layout-col-b">
<article tabindex="-1" aria-label="Main Content">
<h3>Main content goes here.</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis molestie, orci at viverra ornare,
augue urna fermentum ex, vitae dignissim magna est sit amet diam. Nunc sodales dolor finibus
pulvinar placerat. Suspendisse vitae tellus auctor, sodales erat ac, venenatis quam. Etiam
purus est, consequat nec erat vel, bibendum volutpat ex. Fusce vitae consectetur ante.
Suspendisse elit mauris, iaculis sed diam eu, efficitur tempor dui. Praesent tristique nunc
quam, in tincidunt ligula accumsan et. Etiam augue sem, commodo ac ipsum vel, fringilla dapibus
lacus. Sed facilisis euismod felis, non malesuada massa scelerisque sed. Etiam et placerat
lorem. Nullam quis tincidunt sapien.</p>
</article>
</main>
</div>
</div>
</section>
</%block>
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