Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patch release 2022-12-02 #3864

Merged
merged 32 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
60080d6
Remove image from innerHTML on remove.
rtibbles Nov 22, 2022
01b4503
Create remove event mechanism for clearing custom span element.
rtibbles Nov 23, 2022
02216e9
Consolidate all blank space wrapping in custom markdown field logic.
rtibbles Nov 23, 2022
d5b981e
Clean up squire keydown handling.
rtibbles Nov 23, 2022
ff87c48
Modify prop values using web component interface rather than
rtibbles Nov 23, 2022
f1f0a5c
Add extra edge case handling for sibling checking.
rtibbles Nov 23, 2022
48a97be
Tweak markdown conversion to prevent addition of excess line breaks.
rtibbles Nov 23, 2022
7f7e675
Merge pull request #3837 from rtibbles/assessment_images
bjester Nov 23, 2022
b6e08ba
Only enqueue storage calculation task for non-admins
bjester Nov 30, 2022
d2d1921
Add helpers to celery app class for production intervention
bjester Nov 30, 2022
94ae2ac
Add calculation delay to ricecooker bulk endpoint, update tests
bjester Nov 30, 2022
a997d0c
Reorganize task revocation logic
bjester Nov 30, 2022
a331538
Add management command that reconciles tasks for changes
bjester Nov 30, 2022
9d84374
Merge pull request #3852 from bjester/angry-tasks
rtibbles Nov 30, 2022
f0986c7
Fixes `sync_channel` assessment_item bug
vkWeb Dec 2, 2022
bafe68c
[#3827][#3828] Add probers for alerting to task and change abnormalities
bjester Dec 1, 2022
5345841
[#3795] Retain TASK_ID and COPYING_FLAG when fetching from the server
bjester Dec 1, 2022
cfbfe3d
[#3856][#3857] Update axios, silence aborted connection errors, and o…
bjester Dec 2, 2022
f16e24f
[#3845] Defensive check against double submit on channel activation
bjester Dec 2, 2022
a86e0a3
[#3845] Disalbing of channel activation submit button while processing
bjester Dec 2, 2022
fe66038
[#3860] Refactor file upload handling, making return values consisten…
bjester Dec 2, 2022
b714641
[#3856] Add axios to include it for transform in jest
bjester Dec 2, 2022
8bee2fd
[#3860] Defensive check on file duration, which might occur when file…
bjester Dec 2, 2022
706d7d6
Merge pull request #3855 from bjester/mo-patches
bjester Dec 2, 2022
b63e0a3
Merge pull request #3859 from vkWeb/fix/sync_channel_assessment_item
bjester Dec 2, 2022
28398d8
Merge branch 'master' into hotfixes
bjester Dec 2, 2022
ef66578
Fix axios key name for custom param serializer
bjester Dec 5, 2022
7517281
Merge pull request #3866 from bjester/fix-axios-serializer
bjester Dec 5, 2022
b310b20
Revert change that did markdown conversion last to avoid escaped mark…
rtibbles Dec 6, 2022
19c25ff
Prevent error when this.parentNode is already null.
rtibbles Dec 7, 2022
6e25862
Fix application of 'editing' attribute to custom markdown editing com…
rtibbles Dec 7, 2022
88d7008
Merge pull request #3870 from rtibbles/assessment_tweaks
bjester Dec 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contentcuration/contentcuration/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ class DelayUserStorageCalculation(ContextDecorator):
def is_active(self):
return self.depth > 0

def add(self, user_id):
if user_id not in self.queue:
self.queue.append(user_id)

def __enter__(self):
self.depth += 1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,10 +705,11 @@
audioVideoFiles = this.nodeFiles.filter(file => this.allowedFileType(file));
// return the last item in the array
const file = audioVideoFiles[audioVideoFiles.length - 1];
return file.duration;
} else {
return null;
if (file) {
return file.duration;
}
}
return null;
},
videoSelected() {
return this.oneSelected && this.firstNode.kind === ContentKindsNames.VIDEO;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@
data-test="deploy-dialog"
:title="$tr('deployChannel')"
:submitText="$tr('confirmDeployBtn')"
:submitDisabled="submitDisabled"
:cancelDisabled="submitDisabled"
:cancelText="$tr('cancelDeployBtn')"
@submit="onDeployChannelClick"
@cancel="displayDeployDialog = false"
Expand Down Expand Up @@ -293,6 +295,7 @@
displayDeployDialog: false,
drawer: false,
elevated: false,
submitDisabled: false,
};
},
computed: {
Expand Down Expand Up @@ -504,7 +507,13 @@
this.elevated = e.target.scrollTop > 0;
},
async onDeployChannelClick() {
await this.deployCurrentChannel();
this.submitDisabled = true;
try {
await this.deployCurrentChannel();
} catch (e) {
this.submitDisabled = false;
throw e;
}
await this.loadChannel(this.currentChannel.id);

this.$router.push(this.rootTreeRoute);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ export function deployCurrentChannel(context) {
let payload = {
channel_id: context.state.currentChannelId,
};
return client.post(window.Urls.activate_channel(), payload);
return client.post(window.Urls.activate_channel(), payload).catch(e => {
// If response is 'Bad request', channel must already be activated
if (e.response && e.response.status === 400) {
return Promise.resolve();
}
});
}

export function publishChannel(context, version_notes) {
Expand Down
20 changes: 17 additions & 3 deletions contentcuration/contentcuration/frontend/shared/client.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import omit from 'lodash/omit';
import axios from 'axios';
import qs from 'qs';
import * as Sentry from '@sentry/vue';
Expand Down Expand Up @@ -26,7 +27,15 @@ export function paramsSerializer(params) {
const client = axios.create({
xsrfCookieName: 'csrftoken',
xsrfHeaderName: 'X-CSRFToken',
paramsSerializer,
paramsSerializer: {
serialize: paramsSerializer,
},
});

// Track when the browser was last offline for error reporting purposes
let lastOffline = null;
window.addEventListener('offline', () => {
lastOffline = Date.now();
});

client.interceptors.response.use(
Expand Down Expand Up @@ -55,11 +64,12 @@ client.interceptors.response.use(
// In dev build log warnings to console for developer use
console.warn('AJAX Request Error: ' + message); // eslint-disable-line no-console
console.warn('Error data: ', error); // eslint-disable-line no-console
} else {
} else if (error.code !== 'ECONNABORTED') {
Sentry.withScope(function(scope) {
scope.addAttachment({
filename: 'error.json',
data: JSON.stringify(error),
// strip csrf token from headers
data: JSON.stringify(omit(error, ['config.headers.X-CSRFToken'])),
contentType: 'application/json',
});
Sentry.captureException(new Error(message), {
Expand All @@ -69,6 +79,10 @@ client.interceptors.response.use(
method: error.config.method,
url,
},
Network: {
lastOffline: lastOffline ? `${Date.now() - lastOffline}ms ago` : 'never',
online: navigator.onLine,
},
},
});
});
Expand Down
125 changes: 71 additions & 54 deletions contentcuration/contentcuration/frontend/shared/data/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,65 +264,82 @@ class IndexedDBResource {
CHANGES_TABLE,
() => {
// Get any relevant changes that would be overwritten by this bulkPut
return db[CHANGES_TABLE].where('[table+key]')
const changesPromise = db[CHANGES_TABLE].where('[table+key]')
.anyOf(itemData.map(datum => [this.tableName, this.getIdValue(datum)]))
.sortBy('rev', changes => {
changes = mergeAllChanges(changes, true);
const collectedChanges = collectChanges(changes)[this.tableName] || {};
for (let changeType of Object.keys(collectedChanges)) {
const map = {};
for (let change of collectedChanges[changeType]) {
map[change.key] = change;
}
collectedChanges[changeType] = map;
.sortBy('rev');
const currentPromise = this.table
.where(this.idField)
.anyOf(itemData.map(datum => this.getIdValue(datum)))
.toArray();

return Promise.all([changesPromise, currentPromise]).then(([changes, currents]) => {
changes = mergeAllChanges(changes, true);
const collectedChanges = collectChanges(changes)[this.tableName] || {};
for (let changeType of Object.keys(collectedChanges)) {
const map = {};
for (let change of collectedChanges[changeType]) {
map[change.key] = change;
}
const data = itemData
.map(datum => {
datum[LAST_FETCHED] = now;
const id = this.getIdValue(datum);
// If we have an updated change, apply the modifications here
if (
collectedChanges[CHANGE_TYPES.UPDATED] &&
collectedChanges[CHANGE_TYPES.UPDATED][id]
) {
applyMods(datum, collectedChanges[CHANGE_TYPES.UPDATED][id].mods);
collectedChanges[changeType] = map;
}
const currentMap = {};
for (let currentObj of currents) {
currentMap[this.getIdValue(currentObj)] = currentObj;
}
const data = itemData
.map(datum => {
const id = this.getIdValue(datum);
datum[LAST_FETCHED] = now;
// Persist TASK_ID and COPYING_FLAG attributes when directly fetching from the server
if (currentMap[id] && currentMap[id][TASK_ID]) {
datum[TASK_ID] = currentMap[id][TASK_ID];
}
if (currentMap[id] && currentMap[id][COPYING_FLAG]) {
datum[COPYING_FLAG] = currentMap[id][COPYING_FLAG];
}
// If we have an updated change, apply the modifications here
if (
collectedChanges[CHANGE_TYPES.UPDATED] &&
collectedChanges[CHANGE_TYPES.UPDATED][id]
) {
applyMods(datum, collectedChanges[CHANGE_TYPES.UPDATED][id].mods);
}
return datum;
// If we have a deleted change, just filter out this object so we don't reput it
})
.filter(
datum =>
!collectedChanges[CHANGE_TYPES.DELETED] ||
!collectedChanges[CHANGE_TYPES.DELETED][this.getIdValue(datum)]
);
return this.table.bulkPut(data).then(() => {
// Move changes need to be reapplied on top of fetched data in case anything
// has happened on the backend.
return applyChanges(Object.values(collectedChanges[CHANGE_TYPES.MOVED] || {})).then(
results => {
if (!results || !results.length) {
return data;
}
return datum;
// If we have a deleted change, just filter out this object so we don't reput it
})
.filter(
datum =>
!collectedChanges[CHANGE_TYPES.DELETED] ||
!collectedChanges[CHANGE_TYPES.DELETED][this.getIdValue(datum)]
);
return this.table.bulkPut(data).then(() => {
// Move changes need to be reapplied on top of fetched data in case anything
// has happened on the backend.
return applyChanges(Object.values(collectedChanges[CHANGE_TYPES.MOVED] || {})).then(
results => {
if (!results || !results.length) {
return data;
}
const resultsMap = {};
for (let result of results) {
const id = this.getIdValue(result);
resultsMap[id] = result;
}
return data
.map(datum => {
const id = this.getIdValue(datum);
if (resultsMap[id]) {
applyMods(datum, resultsMap[id]);
}
return datum;
// Concatenate any unsynced created objects onto
// the end of the returned objects
})
.concat(Object.values(collectedChanges[CHANGE_TYPES.CREATED]).map(c => c.obj));
const resultsMap = {};
for (let result of results) {
const id = this.getIdValue(result);
resultsMap[id] = result;
}
);
});
return data
.map(datum => {
const id = this.getIdValue(datum);
if (resultsMap[id]) {
applyMods(datum, resultsMap[id]);
}
return datum;
// Concatenate any unsynced created objects onto
// the end of the returned objects
})
.concat(Object.values(collectedChanges[CHANGE_TYPES.CREATED]).map(c => c.obj));
}
);
});
});
}
);
}
Expand Down
Loading