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:
Yusuf Musleh
2024-05-07 20:29:35 +03:00
committed by GitHub
parent 88336c78d6
commit a3924f687b
8 changed files with 121 additions and 21 deletions

View File

@@ -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):

View 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;
}
);

View File

@@ -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();
});

View File

@@ -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({

View File

@@ -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;

View File

@@ -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"

View 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>

View File

@@ -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>