container_subviews.js 10.8 KB
Newer Older
1 2 3
/**
 * Subviews (usually small side panels) for XBlockContainerPage.
 */
4
define(["jquery", "underscore", "gettext", "js/views/baseview", "common/js/components/utils/view_utils",
5 6 7 8
    "js/views/utils/xblock_utils"],
    function ($, _, gettext, BaseView, ViewUtils, XBlockViewUtils) {
        var VisibilityState = XBlockViewUtils.VisibilityState,
            disabledCss = "is-disabled";
9 10

        /**
11
         * A view that refreshes the view when certain values in the XBlockInfo have changed
12 13
         * after a server sync operation.
         */
14
        var ContainerStateListenerView = BaseView.extend({
15 16 17 18 19 20

            // takes XBlockInfo as a model
            initialize: function() {
                this.model.on('sync', this.onSync, this);
            },

21
            onSync: function(model) {
22
                if (this.shouldRefresh(model)) {
23 24 25 26
                   this.render();
                }
            },

27 28 29 30
            shouldRefresh: function(model) {
                return false;
            },

31 32 33
            render: function() {}
        });

34 35 36 37 38 39 40
        var MessageView = ContainerStateListenerView.extend({
            initialize: function () {
                ContainerStateListenerView.prototype.initialize.call(this);
                this.template = this.loadTemplate('container-message');
            },

            shouldRefresh: function(model) {
41
                return ViewUtils.hasChangedAttributes(model, ['currently_visible_to_students']);
42 43 44 45
            },

            render: function() {
                this.$el.html(this.template({
46
                    currentlyVisibleToStudents: this.model.get('currently_visible_to_students')
47 48 49 50 51
                }));
                return this;
            }
        });

52
        /**
53
         * A controller for updating the "View Live" button.
54
         */
55
        var ViewLiveButtonController = ContainerStateListenerView.extend({
56
            shouldRefresh: function(model) {
57
                return ViewUtils.hasChangedAttributes(model, ['published']);
58
            },
59 60

            render: function() {
61
                var viewLiveAction = this.$el.find('.button-view');
62
                if (this.model.get('published')) {
63
                    viewLiveAction.removeClass(disabledCss).attr('aria-disabled', false);
64 65
                }
                else {
66
                    viewLiveAction.addClass(disabledCss).attr('aria-disabled', true);
67 68 69 70 71 72 73 74 75 76 77 78 79 80
                }
            }
        });

        /**
         * Publisher is a view that supports the following:
         * 1) Publishing of a draft version of an xblock.
         * 2) Discarding of edits in a draft version.
         * 3) Display of who last edited the xblock, and when.
         * 4) Display of publish status (published, published with changes, changes with no published version).
         */
        var Publisher = BaseView.extend({
            events: {
                'click .action-publish': 'publish',
81 82
                'click .action-discard': 'discardChanges',
                'click .action-staff-lock': 'toggleStaffLock'
83 84 85 86 87 88 89 90
            },

            // takes XBlockInfo as a model

            initialize: function () {
                BaseView.prototype.initialize.call(this);
                this.template = this.loadTemplate('publish-xblock');
                this.model.on('sync', this.onSync, this);
91
                this.renderPage = this.options.renderPage;
92 93
            },

94
            onSync: function(model) {
95
                if (ViewUtils.hasChangedAttributes(model, [
96 97
                    'has_changes', 'published', 'edited_on', 'edited_by', 'visibility_state',
                    'has_explicit_staff_lock', 'has_content_group_components'
98
                ])) {
99 100 101 102 103 104
                   this.render();
                }
            },

            render: function () {
                this.$el.html(this.template({
105 106 107
                    visibilityState: this.model.get('visibility_state'),
                    visibilityClass: XBlockViewUtils.getXBlockVisibilityClass(this.model.get('visibility_state')),
                    hasChanges: this.model.get('has_changes'),
108 109
                    editedOn: this.model.get('edited_on'),
                    editedBy: this.model.get('edited_by'),
110
                    published: this.model.get('published'),
111 112
                    publishedOn: this.model.get('published_on'),
                    publishedBy: this.model.get('published_by'),
113
                    released: this.model.get('released_to_students'),
114
                    releaseDate: this.model.get('release_date'),
115 116
                    releaseDateFrom: this.model.get('release_date_from'),
                    hasExplicitStaffLock: this.model.get('has_explicit_staff_lock'),
117 118
                    staffLockFrom: this.model.get('staff_lock_from'),
                    hasContentGroupComponents: this.model.get('has_content_group_components')
119 120 121 122 123 124 125 126 127 128
                }));

                return this;
            },

            publish: function (e) {
                var xblockInfo = this.model;
                if (e && e.preventDefault) {
                    e.preventDefault();
                }
Bertrand Marron committed
129
                ViewUtils.runOperationShowingMessage(gettext('Publishing'),
130
                    function () {
cahrens committed
131
                        return xblockInfo.save({publish: 'make_public'}, {patch: true});
132 133
                    }).always(function() {
                        xblockInfo.set("publish", null);
134 135 136 137 138 139
                    }).done(function () {
                        xblockInfo.fetch();
                    });
            },

            discardChanges: function (e) {
140
                var xblockInfo = this.model, renderPage = this.renderPage;
141 142 143
                if (e && e.preventDefault) {
                    e.preventDefault();
                }
144
                ViewUtils.confirmThenRunOperation(gettext("Discard Changes"),
145
                    gettext("Are you sure you want to revert to the last published version of the unit? You cannot undo this action."),
cahrens committed
146 147
                    gettext("Discard Changes"),
                    function () {
Bertrand Marron committed
148
                        ViewUtils.runOperationShowingMessage(gettext('Discarding Changes'),
cahrens committed
149 150 151 152 153 154 155
                            function () {
                                return xblockInfo.save({publish: 'discard_changes'}, {patch: true});
                            }).always(function() {
                                xblockInfo.set("publish", null);
                            }).done(function () {
                                renderPage();
                            });
156
                    }
cahrens committed
157
                );
158 159 160
            },

            toggleStaffLock: function (e) {
161
                var xblockInfo = this.model, self=this, enableStaffLock, hasInheritedStaffLock,
162 163 164 165
                    saveAndPublishStaffLock, revertCheckBox;
                if (e && e.preventDefault) {
                    e.preventDefault();
                }
166 167
                enableStaffLock = !xblockInfo.get('has_explicit_staff_lock');
                hasInheritedStaffLock = xblockInfo.get('ancestor_has_staff_lock');
168 169 170 171 172 173

                revertCheckBox = function() {
                    self.checkStaffLock(!enableStaffLock);
                };

                saveAndPublishStaffLock = function() {
174 175
                    // Setting staff lock to null when disabled will delete the field from this xblock,
                    // allowing it to use the inherited value instead of using false explicitly.
176 177
                    return xblockInfo.save({
                        publish: 'republish',
178
                        metadata: {visible_to_staff_only: enableStaffLock ? true : null}},
179 180 181 182 183 184 185 186 187 188 189
                        {patch: true}
                    ).always(function() {
                        xblockInfo.set("publish", null);
                    }).done(function () {
                        xblockInfo.fetch();
                    }).fail(function() {
                        revertCheckBox();
                    });
                };

                this.checkStaffLock(enableStaffLock);
190
                if (enableStaffLock && !hasInheritedStaffLock) {
Bertrand Marron committed
191
                    ViewUtils.runOperationShowingMessage(gettext('Hiding from Students'),
192 193
                        _.bind(saveAndPublishStaffLock, self));
                } else if (enableStaffLock && hasInheritedStaffLock) {
Bertrand Marron committed
194
                    ViewUtils.runOperationShowingMessage(gettext('Explicitly Hiding from Students'),
195 196
                        _.bind(saveAndPublishStaffLock, self));
                } else if (!enableStaffLock && hasInheritedStaffLock) {
Bertrand Marron committed
197
                    ViewUtils.runOperationShowingMessage(gettext('Inheriting Student Visibility'),
198 199
                        _.bind(saveAndPublishStaffLock, self));
                } else {
200
                    ViewUtils.confirmThenRunOperation(gettext("Make Visible to Students"),
201
                        gettext("If the unit was previously published and released to students, any changes you made to the unit when it was hidden will now be visible to students. Do you want to proceed?"),
202
                        gettext("Make Visible to Students"),
203
                        function() {
Bertrand Marron committed
204
                            ViewUtils.runOperationShowingMessage(gettext('Making Visible to Students'),
205 206 207 208 209 210 211 212 213 214 215
                                _.bind(saveAndPublishStaffLock, self));
                        },
                        function() {
                            // On cancel, revert the check in the check box
                            revertCheckBox();
                        }
                    );
                }
            },

            checkStaffLock: function(check) {
216 217
                this.$('.action-staff-lock i').removeClass('fa-check-square-o fa-square-o');
                this.$('.action-staff-lock i').addClass(check ? 'fa-check-square-o' : 'fa-square-o');
218 219 220
            }
        });

221 222 223 224 225 226 227 228 229 230 231 232
        /**
         * PublishHistory displays when and by whom the xblock was last published, if it ever was.
         */
        var PublishHistory = BaseView.extend({
            // takes XBlockInfo as a model

            initialize: function () {
                BaseView.prototype.initialize.call(this);
                this.template = this.loadTemplate('publish-history');
                this.model.on('sync', this.onSync, this);
            },

233
            onSync: function(model) {
234
                if (ViewUtils.hasChangedAttributes(model, ['published', 'published_on', 'published_by'])) {
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
                   this.render();
                }
            },

            render: function () {
                this.$el.html(this.template({
                    published: this.model.get('published'),
                    published_on: this.model.get('published_on'),
                    published_by: this.model.get('published_by')
                }));

                return this;
            }
        });

250
        return {
251
            'MessageView': MessageView,
252
            'ViewLiveButtonController': ViewLiveButtonController,
253 254
            'Publisher': Publisher,
            'PublishHistory': PublishHistory
255 256
        };
    }); // end define();