Skip to content

Commit 6f4929c

Browse files
authored
Merge pull request learningequality#5729 from AlexVelezLl/draft-tokens
Show draft tokens in Studio
2 parents 93c3534 + 045277a commit 6f4929c

14 files changed

Lines changed: 288 additions & 6 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<template>
2+
3+
<KModal :title="previewYourDraftTitle$()">
4+
<template #default>
5+
<div>
6+
<strong>{{ draftTokenLabel$() }}</strong>
7+
<StudioCopyToken
8+
:token="channel.draft_token"
9+
:showLabel="false"
10+
/>
11+
<p class="mt-16">{{ channelTokenDescription$() }}</p>
12+
</div>
13+
</template>
14+
<template #actions>
15+
<KButton
16+
:text="dismissAction$()"
17+
@click="handleDismiss"
18+
/>
19+
</template>
20+
</KModal>
21+
22+
</template>
23+
24+
25+
<script setup>
26+
27+
import { onMounted } from 'vue';
28+
import StudioCopyToken from 'shared/views/StudioCopyToken';
29+
import { communityChannelsStrings } from 'shared/strings/communityChannelsStrings';
30+
import { commonStrings } from 'shared/strings/commonStrings';
31+
import logging from 'shared/logging';
32+
33+
const props = defineProps({
34+
channel: {
35+
type: Object,
36+
required: true,
37+
},
38+
});
39+
40+
const emit = defineEmits(['close']);
41+
42+
const { dismissAction$ } = commonStrings;
43+
const { previewYourDraftTitle$, draftTokenLabel$, channelTokenDescription$ } =
44+
communityChannelsStrings;
45+
46+
function handleDismiss() {
47+
emit('close');
48+
}
49+
50+
onMounted(() => {
51+
if (!props.channel.draft_token) {
52+
// If there is no draft token, we can't preview the draft, so we just close the modal
53+
logging.error(
54+
'Attempted to open PreviewDraftChannelModal without a draft token. Closing modal.',
55+
{ channelId: props.channel.id },
56+
);
57+
handleDismiss();
58+
}
59+
});
60+
61+
</script>
62+
63+
64+
<style lang="scss" scoped>
65+
66+
.mt-16 {
67+
margin-top: 16px;
68+
}
69+
70+
</style>

contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/PublishSidePanel.vue

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313

1414
<template #default>
1515
<div>
16-
<KRadioButtonGroup>
16+
<component :is="showDraftMode ? 'KRadioButtonGroup' : 'div'">
1717
<KRadioButton
18+
v-if="showDraftMode"
1819
:label="modeLive$()"
1920
:buttonValue="PublishModes.LIVE"
2021
:currentValue="mode"
@@ -26,7 +27,7 @@
2627
<div
2728
v-if="mode === PublishModes.LIVE"
2829
class="live-mode-content"
29-
style="margin-top: 16px; margin-left: 24px"
30+
:style="showDraftMode ? { marginLeft: '24px', marginTop: '16px' } : {}"
3031
>
3132
<div class="live-publish-info">
3233
<KIcon
@@ -126,13 +127,14 @@
126127
</div>
127128

128129
<KRadioButton
130+
v-if="showDraftMode"
129131
:label="modeDraft$()"
130132
:buttonValue="PublishModes.DRAFT"
131133
:currentValue="mode"
132134
:description="modeDraftDescription$()"
133135
@input="mode = PublishModes.DRAFT"
134136
/>
135-
</KRadioButtonGroup>
137+
</component>
136138
</div>
137139
</template>
138140

@@ -166,6 +168,7 @@
166168
import { communityChannelsStrings } from 'shared/strings/communityChannelsStrings';
167169
import { LanguagesList } from 'shared/leUtils/Languages';
168170
import logging from 'shared/logging';
171+
import { FeatureFlagKeys } from 'shared/constants';
169172
170173
export default {
171174
name: 'PublishSidePanel',
@@ -215,12 +218,14 @@
215218
cancelAction$,
216219
languageLabel$,
217220
languageRequiredMessage$,
221+
draftBeingPublishedNotice$,
218222
versionNotesRequiredMessage$,
219223
} = communityChannelsStrings;
220224
221225
const currentChannel = computed(() => store.getters['currentChannel/currentChannel']);
222226
const getContentNode = computed(() => store.getters['contentNode/getContentNode']);
223227
const areAllChangesSaved = computed(() => store.getters['areAllChangesSaved']);
228+
const hasFeatureEnabled = computed(() => store.getters['hasFeatureEnabled']);
224229
225230
const incompleteResourcesCount = computed(() => {
226231
if (!currentChannel.value) return 0;
@@ -259,6 +264,10 @@
259264
return true;
260265
});
261266
267+
const showDraftMode = computed(() =>
268+
hasFeatureEnabled.value(FeatureFlagKeys.test_dev_feature),
269+
);
270+
262271
const submitText = computed(() => {
263272
return mode.value === PublishModes.DRAFT ? saveDraft$() : publishAction$();
264273
});
@@ -364,6 +373,7 @@
364373
await Channel.publishDraft(currentChannel.value.id, {
365374
use_staging_tree: false,
366375
});
376+
store.dispatch('showSnackbarSimple', draftBeingPublishedNotice$());
367377
emit('close');
368378
} else {
369379
// `newChannelLanguage.value` is a KSelect option { value, label }, so we need to
@@ -392,6 +402,7 @@
392402
isChannelLanguageLoading,
393403
PublishModes,
394404
mode,
405+
showDraftMode,
395406
version_notes,
396407
submitting,
397408
languageOptions,

contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/__tests__/PublishSidePanel.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const renderComponent = (props = {}) => {
5151
store.commit('channel/ADD_CHANNEL', currentChannel);
5252
store.commit('contentNode/ADD_CONTENTNODE', rootNode);
5353
store.commit('SET_UNSAVED_CHANGES', props.areAllChangesSaved === false);
54+
store.commit('UPDATE_SESSION', { is_admin: true });
5455

5556
const router = new VueRouter();
5657

contentcuration/contentcuration/frontend/channelEdit/views/TreeView/TreeViewBase.vue

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@
192192
</VListTileTitle>
193193
</VListTile>
194194
</template>
195+
<VListTile
196+
v-if="currentChannel && currentChannel.draft_token"
197+
@click="showPreviewDraftModal = true"
198+
>
199+
<VListTileTitle>{{ getDraftTokenAction$() }}</VListTileTitle>
200+
</VListTile>
195201
<VListTile
196202
v-if="isPublished"
197203
@click="showTokenModal = true"
@@ -277,6 +283,11 @@
277283
@delete="handleDelete"
278284
@close="showDeleteModal = false"
279285
/>
286+
<PreviewDraftChannelModal
287+
v-if="showPreviewDraftModal && currentChannel"
288+
:channel="currentChannel"
289+
@close="showPreviewDraftModal = false"
290+
/>
280291
<VSpeedDial
281292
v-if="showClipboardSpeedDial"
282293
v-model="showClipboard"
@@ -340,6 +351,7 @@
340351
<script>
341352
342353
import { mapActions, mapGetters, mapState } from 'vuex';
354+
import PreviewDraftChannelModal from '../../components/modals/PreviewDraftChannelModal.vue';
343355
import Clipboard from '../../components/Clipboard';
344356
import SyncResourcesModal from '../sync/SyncResourcesModal';
345357
import ProgressModal from '../progress/ProgressModal';
@@ -361,6 +373,8 @@
361373
import DraggableRegion from 'shared/views/draggable/DraggableRegion';
362374
import { DropEffect } from 'shared/mixins/draggable/constants';
363375
import DraggablePlaceholder from 'shared/views/draggable/DraggablePlaceholder';
376+
import { communityChannelsStrings } from 'shared/strings/communityChannelsStrings';
377+
import { commonStrings } from 'shared/strings/commonStrings';
364378
365379
export default {
366380
name: 'TreeViewBase',
@@ -381,8 +395,15 @@
381395
DraggablePlaceholder,
382396
SavingIndicator,
383397
QuickEditModal,
398+
PreviewDraftChannelModal,
384399
},
385400
mixins: [titleMixin],
401+
setup() {
402+
const { getDraftTokenAction$ } = communityChannelsStrings;
403+
return {
404+
getDraftTokenAction$,
405+
};
406+
},
386407
props: {
387408
loading: {
388409
type: Boolean,
@@ -398,6 +419,7 @@
398419
showSyncModal: false,
399420
showClipboard: false,
400421
showDeleteModal: false,
422+
showPreviewDraftModal: false,
401423
syncing: false,
402424
resubmitToCommunityLibraryModalData: null,
403425
};
@@ -533,6 +555,22 @@
533555
},
534556
immediate: true,
535557
},
558+
isDraftPublishing(newVal, oldVal) {
559+
if (!newVal && oldVal) {
560+
const { draftPublishedNotice$ } = communityChannelsStrings;
561+
const { previewAction$ } = commonStrings;
562+
const snackbarData = {
563+
text: draftPublishedNotice$(),
564+
};
565+
if (this.currentChannel.draft_token) {
566+
snackbarData.actionText = previewAction$();
567+
snackbarData.actionCallback = () => {
568+
this.showPreviewDraftModal = true;
569+
};
570+
}
571+
this.$store.dispatch('showSnackbar', snackbarData);
572+
}
573+
},
536574
},
537575
methods: {
538576
...mapActions('channel', ['deleteChannel']),

contentcuration/contentcuration/frontend/shared/composables/__tests__/useChannelVersionHistory.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe('useChannelVersionHistory', () => {
3737
expect(ChannelVersion.fetchCollection).toHaveBeenCalledWith({
3838
channel: 'channel-id',
3939
max_results: VERSIONS_PER_PAGE,
40+
version__gte: 0,
4041
});
4142
expect(versions.value).toEqual(mockVersions);
4243
expect(hasMore.value).toBe(true);

contentcuration/contentcuration/frontend/shared/composables/useChannelVersionHistory.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export function useChannelVersionHistory() {
4242
const response = await ChannelVersion.fetchCollection({
4343
channel: channelId,
4444
max_results: VERSIONS_PER_PAGE,
45+
version__gte: 0, // Exclude unpublished versions with version=null
4546
});
4647

4748
versions.value = response.results || [];

contentcuration/contentcuration/frontend/shared/strings/commonStrings.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,12 @@ export const commonStrings = createTranslator('CommonStrings', {
3030
message: 'Channel Details',
3131
context: 'Label for a section that displays details about a channel',
3232
},
33+
previewAction: {
34+
message: 'Preview',
35+
context: 'A label for an action that opens a preview of content',
36+
},
37+
dismissAction: {
38+
message: 'Dismiss',
39+
context: 'A label for an action that dismisses a notification or message',
40+
},
3341
});

contentcuration/contentcuration/frontend/shared/strings/communityChannelsStrings.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,4 +496,32 @@ export const communityChannelsStrings = createTranslator('CommunityChannelsStrin
496496
message: 'Review',
497497
context: 'Action button to review a channel version submission to the Community Library',
498498
},
499+
500+
// Draft channel strings
501+
draftBeingPublishedNotice: {
502+
message: 'Draft version is being published',
503+
context: 'Label indicating that a draft version of the channel is currently being published',
504+
},
505+
draftPublishedNotice: {
506+
message: 'Draft published successfully',
507+
context: 'Label indicating that a draft version of the channel has been successfully published',
508+
},
509+
previewYourDraftTitle: {
510+
message: 'Preview your draft channel in Kolibri',
511+
context: 'Title for the modal that shows instructions to preview a draft channel in Kolibri',
512+
},
513+
channelTokenDescription: {
514+
message:
515+
'To preview your draft channel right away, simply copy the unique draft channel token. This is the sole method to access the channel.',
516+
context: 'Description for the channel token field in the draft preview instructions modal',
517+
},
518+
getDraftTokenAction: {
519+
message: 'Get draft token',
520+
context:
521+
'Button text for the action to retrieve the draft token in the draft preview instructions modal',
522+
},
523+
draftTokenLabel: {
524+
message: 'Draft token',
525+
context: 'Label for the draft token field in the draft preview instructions modal',
526+
},
499527
});

contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626
>
2727
<VTab
2828
class="px-3"
29+
href="#"
2930
:disabled="isBusy"
3031
:value="NotificationsTab.UNREAD"
3132
>
3233
{{ unreadNotificationsLabel$() }}
3334
</VTab>
3435
<VTab
3536
class="px-3"
37+
href="#"
3638
:disabled="isBusy"
3739
:value="NotificationsTab.ALL"
3840
>

contentcuration/contentcuration/frontend/shared/views/details/StudioDetailsPanel.vue

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,23 @@
4444
:showLabel="false"
4545
/>
4646
<span v-else>
47-
{{ _details.primary_token.slice(0, 5) + '-' + _details.primary_token.slice(5) }}
47+
{{ hyphenateToken(_details.primary_token) }}
48+
</span>
49+
</template>
50+
</StudioDetailsRow>
51+
<StudioDetailsRow
52+
v-if="_details.draft_token"
53+
:label="draftTokenLabel$()"
54+
>
55+
<template #default>
56+
<StudioCopyToken
57+
v-if="!printing"
58+
:token="_details.draft_token"
59+
:style="{ maxWidth: 'max-content' }"
60+
:showLabel="false"
61+
/>
62+
<span v-else>
63+
{{ hyphenateToken(_details.draft_token) }}
4864
</span>
4965
</template>
5066
</StudioDetailsRow>
@@ -421,6 +437,8 @@
421437
import StudioDetailsRow from './StudioDetailsRow';
422438
import StudioThumbnail from 'shared/views/files/StudioThumbnail';
423439
import StudioCopyToken from 'shared/views/StudioCopyToken';
440+
import useToken from 'shared/composables/useToken';
441+
import { communityChannelsStrings } from 'shared/strings/communityChannelsStrings';
424442
425443
const DEFAULT_DETAILS = {
426444
name: '',
@@ -473,6 +491,14 @@
473491
titleMixin,
474492
metadataTranslationMixin,
475493
],
494+
setup() {
495+
const { hyphenateToken } = useToken();
496+
const { draftTokenLabel$ } = communityChannelsStrings;
497+
return {
498+
hyphenateToken,
499+
draftTokenLabel$,
500+
};
501+
},
476502
props: {
477503
// Object matching that returned by the channel details and
478504
// node details API endpoints, see backend for details of the

0 commit comments

Comments
 (0)