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

Adjust 'this person can edit this widget' checks on My Widgets page, locks widget instances in creator. #49

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
17 changes: 17 additions & 0 deletions src/js/controllers/ctrl-create.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,19 @@ app.controller('createCtrl', function(
}
}

const checkUserPublishPerms = widgetData => {
const deferred = $q.defer()
widgetSrv.canBePublishedByCurrentUser(widget_id).then(canPublish => {
$scope.canPublish = canPublish

if (!widgetData.is_draft && !canPublish)
deferred.reject('Widget type can not be edited by students after publishing.')

deferred.resolve(widgetData)
})
return deferred.promise
}

// build a my-widgets url to a specific widget
const getMyWidgetsUrl = instid => `${BASE_URL}my-widgets#${instid}`

Expand Down Expand Up @@ -521,6 +534,7 @@ ${msg.toLowerCase()}`,
$scope.saveText = 'Save Draft'
$scope.previewText = 'Preview'
$scope.publishText = 'Publish...'
$scope.canPublish = false
$scope.invalid = false
$scope.modal = false
$scope.requestSave = _requestSave
Expand All @@ -542,7 +556,9 @@ ${msg.toLowerCase()}`,
getQset().then(() => {
if (!$scope.invalid) {
$q(resolve => resolve(inst_id))
.then(widgetSrv.lockWidget)
.then(widgetSrv.getWidget)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking we should try to do lockWidget, getWidget, and checkUserPublishPerms in parallel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure - if the widget is locked then we shouldn't try to get it, and if we can't get it then we shouldn't bother checking whether the user can publish it. That, and checkUserPublishPerms relies on the full widget data being passed into it from widgetSrv.getWidget.

.then(checkUserPublishPerms)
.then(embed)
.then(initCreator)
.then(showButtons)
Expand All @@ -554,6 +570,7 @@ ${msg.toLowerCase()}`,
// initialize a new creator
$q(resolve => resolve(widget_id))
.then(widgetSrv.getWidgetInfo)
.then(checkUserPublishPerms)
.then(embed)
.then(initCreator)
.then(showButtons)
Expand Down
7 changes: 5 additions & 2 deletions src/js/controllers/ctrl-my-widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ app.controller('MyWidgetsController', function(
guestAccess: false,
embeddedOnly: false
}
$scope.perms = { collaborators: [] }
$scope.perms = {
collaborators: []
}
$scope.show = {
collaborationModal: false,
availabilityModal: false,
Expand All @@ -266,7 +268,8 @@ app.controller('MyWidgetsController', function(
exportModal: false,
deleteDialog: false,
embedToggle: false,
editPublishedWarning: false
editPublishedWarning: false,
restrictedPublishWarning: false
}
$scope.SCORE_TAB_GRAPH = 0
$scope.SCORE_TAB_INDIVIDUAL = 1
Expand Down
40 changes: 27 additions & 13 deletions src/js/controllers/ctrl-selectedwidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,28 @@ app.controller('SelectedWidgetController', function(
})
}

const _editWidget = () => {
if ($scope.selected.editable) {
Materia.Coms.Json.send('widget_instance_lock', [$scope.selected.widget.id]).then(success => {
if (success) {
if ($scope.selected.widget.is_draft) {
window.location = $scope.selected.edit
} else {
$scope.show.editPublishedWarning = true
}
const _editWidgetPromise = () => {
return Materia.Coms.Json.send('widget_instance_edit_perms_verify', [
$scope.selected.widget.id
]).then(response => {
if (response.is_locked) {
$scope.alert.msg =
'This widget is currently locked, you will be able to edit this widget when it is no longer being edited by somebody else.'
FrenjaminBanklin marked this conversation as resolved.
Show resolved Hide resolved
} else {
if ($scope.selected.widget.is_draft) {
window.location = $scope.selected.edit
} else {
$scope.alert.msg =
'This widget is currently locked, you will be able to edit this widget when it is no longer being edited by somebody else.'
if (response.can_publish) $scope.show.editPublishedWarning = true
else $scope.show.restrictedPublishWarning = true
}
Please.$apply()
})
}
Please.$apply()
})
}

const _editWidget = () => {
if ($scope.selected.editable) {
_editWidgetPromise()
}

return false
Expand Down Expand Up @@ -225,4 +232,11 @@ app.controller('SelectedWidgetController', function(
$scope.enableOlderScores = _enableOlderScores

$scope.alert = Alert

/* develblock:start */
// these method are exposed for testing
$scope.jestTest = {
_editWidgetPromise
}
/* develblock:end */
})
33 changes: 33 additions & 0 deletions src/js/controllers/ctrl-selectedwidget.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ describe('adminWidgetController', () => {
$on: jest.fn()
}

Namespace('Materia.Coms.Json').send = jest.fn()

var controller = $controller('SelectedWidgetController', { $scope })
})

Expand Down Expand Up @@ -67,4 +69,35 @@ describe('adminWidgetController', () => {
$scope.hideModal.bind(this)()
expect(this.$parent.hideModal).toHaveBeenCalled()
})

it('does nothing if a widget is not editable', () => {
$scope.selected = {
editable: false
}

$scope.editWidget()

expect(Materia.Coms.Json.send).not.toHaveBeenCalled()
})

it('sets an alert message if the widget is locked already', () => {
$scope.selected = {
editable: true,
widget: {
id: 1
}
}

Namespace('Materia.Coms.Json').send = jest.fn().mockResolvedValueOnce({
is_locked: true,
can_publish: true
})

return $scope.jestTest._editWidgetPromise().then(() => {
expect(Materia.Coms.Json.send).toHaveBeenCalledWith('widget_instance_edit_perms_verify', [1])
expect($scope.alert.msg).toBe(
'This widget is currently locked, you will be able to edit this widget when it is no longer being edited by somebody else.'
FrenjaminBanklin marked this conversation as resolved.
Show resolved Hide resolved
)
})
})
})
27 changes: 26 additions & 1 deletion src/js/services/srv-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ app.service('widgetSrv', function(selectedWidgetSrv, dateTimeServ, $q, $rootScop
return Materia.Coms.Json.send('widgets_get', [[id]]).then(widgets => widgets[0])
}

const lockWidget = (id = null) => {
const deferred = $q.defer()
Materia.Coms.Json.send('widget_instance_lock', [id]).then(success => {
if (success) {
deferred.resolve(id)
} else {
deferred.reject(
'This widget is currently locked, you will be able to edit this widget when it is no longer being edited by somebody else.'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exact string is in ctrl-my-widgets.js. Can we de-duplicate it?

)
}
})
return deferred.promise
}

const getWidgetsByType = (type = 'featured') => {
return Materia.Coms.Json.send('widgets_get_by_type', [type])
}
Expand Down Expand Up @@ -233,18 +247,29 @@ app.service('widgetSrv', function(selectedWidgetSrv, dateTimeServ, $q, $rootScop
}
}

const canBePublishedByCurrentUser = widget_id => {
const deferred = $q.defer()
Materia.Coms.Json.send('widget_publish_perms_verify', [widget_id]).then(response => {
deferred.resolve(response)
})

return deferred.promise
}

return {
getWidgets,
getWidgetsByType,
getWidget,
getWidgetInfo,
lockWidget,
sortWidgets,
saveWidget,
removeWidget,
updateHashUrl,
selectWidgetFromHashUrl,
convertAvailibilityDates,
copyWidget,
deleteWidget
deleteWidget,
canBePublishedByCurrentUser
}
})
46 changes: 46 additions & 0 deletions src/js/services/srv-widget.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ describe('widgetSrv', () => {
expect(_service.getWidgetsByType).toBeDefined()
expect(_service.getWidget).toBeDefined()
expect(_service.getWidgetInfo).toBeDefined()
expect(_service.lockWidget).toBeDefined()
expect(_service.sortWidgets).toBeDefined()
expect(_service.saveWidget).toBeDefined()
expect(_service.removeWidget).toBeDefined()
Expand Down Expand Up @@ -401,4 +402,49 @@ describe('widgetSrv', () => {
$scope.$digest() // processes promise
expect(_selectedWidgetSrv.notifyAccessDenied).toHaveBeenCalled()
})

it('rejects with a message when a widget is already locked', () => {
mockSendPromiseOnce(false)

let promiseSpy = jest.fn()
let promiseCatch = jest.fn()
_service
.lockWidget(1)
.then(promiseSpy)
.catch(promiseCatch)
$scope.$digest() // processes promise

expect(Materia.Coms.Json.send).toHaveBeenCalledWith('widget_instance_lock', [1])
expect(promiseSpy).not.toHaveBeenCalled()
expect(promiseCatch).toHaveBeenCalledWith(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting approach I hadn't thought of. I think it's ok as is, however one shouldn't have to test that promiseSpy wasn't called - if promiseCatch ends up getting called promiseSpy will always be skipped.

FWIW jest has another way to test catching with expect().rejects https://jestjs.io/docs/en/tutorial-async#rejects. Though I realize this is using angular's promise lib - which may not be compatible

'This widget is currently locked, you will be able to edit this widget when it is no longer being edited by somebody else.'
)
})

it('locks a widget', () => {
mockSendPromiseOnce(true)

let promiseSpy = jest.fn()
let promiseCatch = jest.fn()
_service
.lockWidget(1)
.then(promiseSpy)
.catch(promiseCatch)
$scope.$digest() // processes promise

expect(Materia.Coms.Json.send).toHaveBeenCalledWith('widget_instance_lock', [1])
expect(promiseSpy).toHaveBeenCalledWith(1)
expect(promiseCatch).not.toHaveBeenCalled()
})

it('checks whether a widget can be published by the current user', () => {
mockSendPromiseOnce(true)

let promiseSpy = jest.fn()
_service.canBePublishedByCurrentUser(1).then(promiseSpy)
$scope.$digest() // processes promise

expect(Materia.Coms.Json.send).toHaveBeenCalledWith('widget_publish_perms_verify', [1])
expect(promiseSpy).toHaveBeenCalledWith(true)
})
})