feat: Tag sections, subsections, and the whole course (#34690)
(In the legacy UI, if the 'new_studio_mfe.use_tagging_taxonomy_list_page' waffle flag is enabled)
This commit is contained in:
@@ -1243,7 +1243,8 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements
|
||||
# If the ENABLE_TAGGING_TAXONOMY_LIST_PAGE feature flag is enabled, we show the "Manage Tags" options
|
||||
if use_tagging_taxonomy_list_page():
|
||||
xblock_info["use_tagging_taxonomy_list_page"] = True
|
||||
xblock_info["tag_counts_by_unit"] = _get_course_unit_tags(xblock.location.context_key)
|
||||
xblock_info["course_tags_count"] = _get_course_tags_count(course.id)
|
||||
xblock_info["tag_counts_by_block"] = _get_course_block_tags(xblock.location.context_key)
|
||||
|
||||
xblock_info[
|
||||
"has_partition_group_components"
|
||||
@@ -1263,16 +1264,29 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements
|
||||
|
||||
|
||||
@request_cached()
|
||||
def _get_course_unit_tags(course_key) -> dict:
|
||||
def _get_course_tags_count(course_key) -> dict:
|
||||
"""
|
||||
Get the count of tags that are applied to each unit (vertical) in this course, as a dict.
|
||||
Get the count of tags that are applied to the course as a dict: {course_key: tags_count}
|
||||
"""
|
||||
if not course_key.is_course:
|
||||
return {} # Unsupported key type
|
||||
|
||||
return get_object_tag_counts(str(course_key), count_implicit=True)
|
||||
|
||||
|
||||
@request_cached()
|
||||
def _get_course_block_tags(course_key) -> dict:
|
||||
"""
|
||||
Get the count of tags that are applied to each block in this course, as a dict.
|
||||
"""
|
||||
if not course_key.is_course:
|
||||
return {} # Unsupported key type, e.g. a library
|
||||
# Create a pattern to match the IDs of the units, e.g. "block-v1:org+course+run+type@vertical+block@*"
|
||||
vertical_key = course_key.make_usage_key('vertical', 'x')
|
||||
unit_key_pattern = str(vertical_key).rsplit("@", 1)[0] + "@*"
|
||||
return get_object_tag_counts(unit_key_pattern, count_implicit=True)
|
||||
|
||||
# Create a pattern to match the IDs of all blocks, e.g. "block-v1:org+course+run+type@*"
|
||||
catch_all_key = course_key.make_usage_key("*", "x")
|
||||
catch_all_key_pattern = str(catch_all_key).rsplit("@*", 1)[0] + "@*"
|
||||
|
||||
return get_object_tag_counts(catch_all_key_pattern, count_implicit=True)
|
||||
|
||||
|
||||
def _was_xblock_ever_exam_linked_with_external(course, xblock):
|
||||
|
||||
54
cms/static/js/views/course_manage_tags.js
Normal file
54
cms/static/js/views/course_manage_tags.js
Normal file
@@ -0,0 +1,54 @@
|
||||
define([
|
||||
'jquery', 'underscore', 'backbone', 'js/utils/templates',
|
||||
'edx-ui-toolkit/js/utils/html-utils', 'js/views/utils/tagging_drawer_utils',
|
||||
'js/views/tag_count', 'js/models/tag_count'],
|
||||
function(
|
||||
$, _, Backbone, TemplateUtils, HtmlUtils, TaggingDrawerUtils, TagCountView, TagCountModel
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var CourseManageTagsView = Backbone.View.extend({
|
||||
events: {
|
||||
'click .manage-tags-button': 'openManageTagsDrawer',
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.template = TemplateUtils.loadTemplate('course-manage-tags');
|
||||
this.courseId = course.id;
|
||||
},
|
||||
|
||||
openManageTagsDrawer: function(event) {
|
||||
const taxonomyTagsWidgetUrl = this.model.get('taxonomy_tags_widget_url');
|
||||
const contentId = this.courseId;
|
||||
TaggingDrawerUtils.openDrawer(taxonomyTagsWidgetUrl, contentId);
|
||||
},
|
||||
|
||||
renderTagCount: function() {
|
||||
const contentId = this.courseId;
|
||||
const tagCountsForCourse = this.model.get('course_tags_count');
|
||||
const tagsCount = tagCountsForCourse !== undefined ? tagCountsForCourse[contentId] : 0;
|
||||
var countModel = new TagCountModel({
|
||||
content_id: contentId,
|
||||
tags_count: tagsCount,
|
||||
course_authoring_url: this.model.get('course_authoring_url'),
|
||||
}, {parse: true});
|
||||
var tagCountView = new TagCountView({el: this.$('.tag-count'), model: countModel});
|
||||
tagCountView.setupMessageListener();
|
||||
tagCountView.render();
|
||||
this.$('.tag-count').click((event) => {
|
||||
event.preventDefault();
|
||||
this.openManageTagsDrawer();
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var html = this.template(this.model.attributes);
|
||||
HtmlUtils.setHtml(this.$el, HtmlUtils.HTML(html));
|
||||
this.renderTagCount();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return CourseManageTagsView;
|
||||
}
|
||||
);
|
||||
@@ -34,17 +34,22 @@ function(
|
||||
|
||||
renderTagCount: function() {
|
||||
const contentId = this.model.get('id');
|
||||
const tagCountsByUnit = this.model.get('tag_counts_by_unit')
|
||||
const tagsCount = tagCountsByUnit !== undefined ? tagCountsByUnit[contentId] : 0
|
||||
const tagCountsByBlock = this.model.get('tag_counts_by_block')
|
||||
// Skip the course block since that is handled elsewhere in course_manage_tags
|
||||
if (contentId.includes('@course')) {
|
||||
return
|
||||
}
|
||||
const tagsCount = tagCountsByBlock !== undefined ? tagCountsByBlock[contentId] : 0
|
||||
const tagCountElem = this.$(`.tag-count[data-locator="${contentId}"]`);
|
||||
var countModel = new TagCountModel({
|
||||
content_id: contentId,
|
||||
tags_count: tagsCount,
|
||||
course_authoring_url: this.model.get('course_authoring_url'),
|
||||
}, {parse: true});
|
||||
var tagCountView = new TagCountView({el: this.$('.tag-count'), model: countModel});
|
||||
var tagCountView = new TagCountView({el: tagCountElem, model: countModel});
|
||||
tagCountView.setupMessageListener();
|
||||
tagCountView.render();
|
||||
this.$('.tag-count').click((event) => {
|
||||
tagCountElem.click((event) => {
|
||||
event.preventDefault();
|
||||
this.openManageTagsDrawer();
|
||||
});
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
define([
|
||||
'jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'js/views/utils/xblock_utils',
|
||||
'js/views/course_outline', 'common/js/components/utils/view_utils', 'common/js/components/views/feedback_alert',
|
||||
'common/js/components/views/feedback_notification', 'js/views/course_highlights_enable', 'js/views/course_video_sharing_enable'],
|
||||
'common/js/components/views/feedback_notification', 'js/views/course_highlights_enable', 'js/views/course_video_sharing_enable',
|
||||
'js/views/course_manage_tags'],
|
||||
function($, _, gettext, BasePage, XBlockViewUtils, CourseOutlineView, ViewUtils, AlertView, NoteView,
|
||||
CourseHighlightsEnableView,
|
||||
CourseVideoSharingEnableView
|
||||
CourseVideoSharingEnableView,
|
||||
CourseManageTagsView
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
@@ -135,6 +137,15 @@ function($, _, gettext, BasePage, XBlockViewUtils, CourseOutlineView, ViewUtils,
|
||||
this.highlightsEnableView.render();
|
||||
}
|
||||
|
||||
// if tagging enabled
|
||||
if (this.model.get('use_tagging_taxonomy_list_page')) {
|
||||
this.courseManageTagsView = new CourseManageTagsView({
|
||||
el: this.$('.status-manage-tags'),
|
||||
model: this.model
|
||||
});
|
||||
this.courseManageTagsView.render();
|
||||
}
|
||||
|
||||
// if video sharing enable
|
||||
if (this.model.get('video_sharing_enabled')) {
|
||||
this.videoSharingEnableView = new CourseVideoSharingEnableView({
|
||||
|
||||
@@ -185,6 +185,7 @@
|
||||
|
||||
.status-release,
|
||||
.status-highlights-enabled,
|
||||
.status-manage-tags,
|
||||
.status-video-sharing-enabled,
|
||||
.status-studio-frontend {
|
||||
@extend %t-copy-base;
|
||||
@@ -202,6 +203,7 @@
|
||||
}
|
||||
|
||||
.status-highlights-enabled,
|
||||
.status-manage-tags,
|
||||
.status-video-sharing-enabled {
|
||||
vertical-align: top;
|
||||
}
|
||||
@@ -209,9 +211,12 @@
|
||||
.status-release-label,
|
||||
.status-release-value,
|
||||
.status-highlights-enabled-label,
|
||||
.status-course-manage-tags-label,
|
||||
.status-video-sharing-enabled-label,
|
||||
.status-highlights-enabled-value,
|
||||
.status-course-manage-tags-value,
|
||||
.status-highlights-enabled-info,
|
||||
.status-course-manage-tags-info,
|
||||
.status-video-sharing-enabled-info,
|
||||
.status-actions {
|
||||
display: inline-block;
|
||||
@@ -221,6 +226,7 @@
|
||||
|
||||
.status-release-value,
|
||||
.status-highlights-enabled-value,
|
||||
.status-course-manage-tags-value,
|
||||
.status-video-sharing-enabled-value {
|
||||
@extend %t-strong;
|
||||
|
||||
@@ -228,6 +234,7 @@
|
||||
}
|
||||
|
||||
.status-highlights-enabled-info,
|
||||
.status-course-manage-tags-info,
|
||||
.status-video-sharing-enabled-info {
|
||||
font-size: smaller;
|
||||
margin-left: $baseline / 2;
|
||||
|
||||
@@ -29,7 +29,7 @@ from django.urls import reverse
|
||||
|
||||
<%block name="header_extras">
|
||||
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
|
||||
% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'self-paced-due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'unit-access-editor', 'discussion-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs', 'show-correctness-editor', 'highlights-editor', 'highlights-enable-editor', 'course-highlights-enable', 'course-video-sharing-enable', 'summary-configuration-editor', 'tag-count', 'subsection-share-link-modal-tabs', 'full-page-share-link-editor', 'embed-link-share-link-editor']:
|
||||
% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'self-paced-due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'unit-access-editor', 'discussion-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs', 'show-correctness-editor', 'highlights-editor', 'highlights-enable-editor', 'course-highlights-enable', 'course-manage-tags', 'course-video-sharing-enable', 'summary-configuration-editor', 'tag-count', 'subsection-share-link-modal-tabs', 'full-page-share-link-editor', 'embed-link-share-link-editor']:
|
||||
<script type="text/template" id="${template_name}-tpl">
|
||||
<%static:include path="js/${template_name}.underscore" />
|
||||
</script>
|
||||
@@ -269,6 +269,7 @@ from django.urls import reverse
|
||||
</%static:studiofrontend>
|
||||
</div>
|
||||
<div class="status-highlights-enabled"></div>
|
||||
<div class="status-manage-tags"></div>
|
||||
<div class="status-video-sharing-enabled"></div>
|
||||
</div>
|
||||
<div class="wrapper-dnd"
|
||||
|
||||
8
cms/templates/js/course-manage-tags.underscore
Normal file
8
cms/templates/js/course-manage-tags.underscore
Normal file
@@ -0,0 +1,8 @@
|
||||
<div class="course-manage-tags-container">
|
||||
<h2 class="status-course-manage-tags-label">
|
||||
<%- gettext('Course tags') %>
|
||||
</h2>
|
||||
<br>
|
||||
<span class="status-course-manage-tags-value tag-count" data-locator="<%- course.id %>"></span>
|
||||
<a class="status-course-manage-tags-info manage-tags-button" href="#"><%- gettext('Manage tags') %></a>
|
||||
</div>
|
||||
@@ -8,7 +8,7 @@ var userPartitionInfo = xblockInfo.get('user_partition_info');
|
||||
var selectedGroupsLabel = userPartitionInfo['selected_groups_label'];
|
||||
var selectedPartitionIndex = userPartitionInfo['selected_partition_index'];
|
||||
var xblockId = xblockInfo.get('id')
|
||||
var tagsCount = (xblockInfo.get('tag_counts_by_unit') || {})[xblockId] || 0;
|
||||
var tagsCount = (xblockInfo.get('tag_counts_by_block') || {})[xblockId] || 0;
|
||||
|
||||
var statusMessages = [];
|
||||
var messageType;
|
||||
@@ -187,7 +187,7 @@ if (is_proctored_exam) {
|
||||
</li>
|
||||
<% } %>
|
||||
|
||||
<% if (xblockInfo.isVertical() && typeof useTaggingTaxonomyListPage !== "undefined" && useTaggingTaxonomyListPage) { %>
|
||||
<% if (typeof useTaggingTaxonomyListPage !== "undefined" && useTaggingTaxonomyListPage) { %>
|
||||
<li class="action-item tag-count" data-locator="<%- xblockId %>"></li>
|
||||
<% } %>
|
||||
|
||||
@@ -210,12 +210,12 @@ if (is_proctored_exam) {
|
||||
<a class="configure-button" href="#" role="button"><%- gettext('Configure') %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if (typeof useTaggingTaxonomyListPage !== "undefined" && useTaggingTaxonomyListPage) { %>
|
||||
<li class="nav-item">
|
||||
<a class="manage-tags-button" href="#" role="button"><%- gettext('Manage Tags') %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if (xblockInfo.isVertical()) { %>
|
||||
<% if (typeof useTaggingTaxonomyListPage !== "undefined" && useTaggingTaxonomyListPage) { %>
|
||||
<li class="nav-item">
|
||||
<a class="manage-tags-button" href="#" role="button"><%- gettext('Manage Tags') %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
<li class="nav-item">
|
||||
<a class="copy-button" href="#" role="button"><%- gettext('Copy to Clipboard') %></a>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user