Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('moodle-course-management', function (Y, NAME) {
2
 
3
var Category;
4
var Console;
5
var Course;
6
var DragDrop;
7
var Item;
8
/**
9
 * Provides drop down menus for list of action links.
10
 *
11
 * @module moodle-course-management
12
 */
13
 
14
/**
15
 * Management JS console.
16
 *
17
 * Provides the organisation for course and category management JS.
18
 *
19
 * @namespace M.course.management
20
 * @class Console
21
 * @constructor
22
 * @extends Base
23
 */
24
Console = function() {
25
    Console.superclass.constructor.apply(this, arguments);
26
};
27
Console.NAME = 'moodle-course-management';
28
Console.CSS_PREFIX = 'management';
29
Console.ATTRS = {
30
    /**
31
     * The HTML element containing the management interface.
32
     * @attribute element
33
     * @type Node
34
     */
35
    element: {
36
        setter: function(node) {
37
            if (typeof node === 'string') {
38
                node = Y.one('#' + node);
39
            }
40
            return node;
41
        }
42
    },
43
 
44
    /**
45
     * The category listing container node.
46
     * @attribute categorylisting
47
     * @type Node
48
     * @default null
49
     */
50
    categorylisting: {
51
        value: null
52
    },
53
 
54
    /**
55
     * The course listing container node.
56
     * @attribute courselisting
57
     * @type Node
58
     * @default null
59
     */
60
    courselisting: {
61
        value: null
62
    },
63
 
64
    /**
65
     * The course details container node.
66
     * @attribute coursedetails
67
     * @type Node|null
68
     * @default null
69
     */
70
    coursedetails: {
71
        value: null
72
    },
73
 
74
    /**
75
     * The id of the currently active category.
76
     * @attribute activecategoryid
77
     * @type Number
78
     * @default null
79
     */
80
    activecategoryid: {
81
        value: null
82
    },
83
 
84
    /**
85
     * The id of the currently active course.
86
     * @attribute activecourseid
87
     * @type Number
88
     * @default Null
89
     */
90
    activecourseid: {
91
        value: null
92
    },
93
 
94
    /**
95
     * The categories that are currently available through the management interface.
96
     * @attribute categories
97
     * @type Array
98
     * @default []
99
     */
100
    categories: {
101
        setter: function(item, name) {
102
            if (Y.Lang.isArray(item)) {
103
                return item;
104
            }
105
            var items = this.get(name);
106
            items.push(item);
107
            return items;
108
        },
109
        value: []
110
    },
111
 
112
    /**
113
     * The courses that are currently available through the management interface.
114
     * @attribute courses
115
     * @type Course[]
116
     * @default Array
117
     */
118
    courses: {
119
        validator: function(val) {
120
            return Y.Lang.isArray(val);
121
        },
122
        value: []
123
    },
124
 
125
    /**
126
     * The currently displayed page of courses.
127
     * @attribute page
128
     * @type Number
129
     * @default null
130
     */
131
    page: {
132
        getter: function(value, name) {
133
            if (value === null) {
134
                value = this.get('element').getData(name);
135
                this.set(name, value);
136
            }
137
            return value;
138
        },
139
        value: null
140
    },
141
 
142
    /**
143
     * The total pages of courses that can be shown for this category.
144
     * @attribute totalpages
145
     * @type Number
146
     * @default null
147
     */
148
    totalpages: {
149
        getter: function(value, name) {
150
            if (value === null) {
151
                value = this.get('element').getData(name);
152
                this.set(name, value);
153
            }
154
            return value;
155
        },
156
        value: null
157
    },
158
 
159
    /**
160
     * The total number of courses belonging to this category.
161
     * @attribute totalcourses
162
     * @type Number
163
     * @default null
164
     */
165
    totalcourses: {
166
        getter: function(value, name) {
167
            if (value === null) {
168
                value = this.get('element').getData(name);
169
                this.set(name, value);
170
            }
171
            return value;
172
        },
173
        value: null
174
    },
175
 
176
    /**
177
     * The URL to use for AJAX actions/requests.
178
     * @attribute ajaxurl
179
     * @type String
180
     * @default /course/ajax/management.php
181
     */
182
    ajaxurl: {
183
        getter: function(value) {
184
            if (value === null) {
185
                value = M.cfg.wwwroot + '/course/ajax/management.php';
186
            }
187
            return value;
188
        },
189
        value: null
190
    },
191
 
192
    /**
193
     * The drag drop handler
194
     * @attribute dragdrop
195
     * @type DragDrop
196
     * @default null
197
     */
198
    dragdrop: {
199
        value: null
200
    }
201
};
202
Console.prototype = {
203
 
204
    /**
205
     * Gets set to true once the first categories have been initialised.
206
     * @property categoriesinit
207
     * @private
208
     * @type {boolean}
209
     */
210
    categoriesinit: false,
211
 
212
    /**
213
     * Initialises a new instance of the Console.
214
     * @method initializer
215
     */
216
    initializer: function() {
217
        this.set('element', 'coursecat-management');
218
        var element = this.get('element'),
219
            categorylisting = element.one('#category-listing'),
220
            courselisting = element.one('#course-listing'),
221
            selectedcategory = null,
222
            selectedcourse = null;
223
 
224
        if (categorylisting) {
225
            selectedcategory = categorylisting.one('.listitem[data-selected="1"]');
226
        }
227
        if (courselisting) {
228
            selectedcourse = courselisting.one('.listitem[data-selected="1"]');
229
        }
230
        this.set('categorylisting', categorylisting);
231
        this.set('courselisting', courselisting);
232
        this.set('coursedetails', element.one('#course-detail'));
233
        if (selectedcategory) {
234
            this.set('activecategoryid', selectedcategory.getData('id'));
235
        }
236
        if (selectedcourse) {
237
            this.set('activecourseid', selectedcourse.getData('id'));
238
        }
239
        this.initialiseCategories(categorylisting);
240
        this.initialiseCourses();
241
 
242
        if (courselisting) {
243
            // No need for dragdrop if we don't have a course listing.
244
            this.set('dragdrop', new DragDrop({console: this}));
245
        }
246
    },
247
 
248
    /**
249
     * Initialises all the categories being shown.
250
     * @method initialiseCategories
251
     * @private
252
     * @return {boolean}
253
     */
254
    initialiseCategories: function(listing) {
255
        var count = 0;
256
        if (!listing) {
257
            return false;
258
        }
259
 
260
        // Disable category bulk actions as nothing will be selected on initialise.
261
        var menumovecatto = listing.one('#menumovecategoriesto');
262
        if (menumovecatto) {
263
            menumovecatto.setAttribute('disabled', true);
264
        }
265
        var menuresortcategoriesby = listing.one('#menuresortcategoriesby');
266
        if (menuresortcategoriesby) {
267
            menuresortcategoriesby.setAttribute('disabled', true);
268
        }
269
        var menuresortcoursesby = listing.one('#menuresortcoursesby');
270
        if (menuresortcoursesby) {
271
            menuresortcoursesby.setAttribute('disabled', true);
272
        }
273
 
274
        listing.all('.listitem[data-id]').each(function(node) {
275
            this.set('categories', new Category({
276
                node: node,
277
                console: this
278
            }));
279
            count++;
280
        }, this);
281
        if (!this.categoriesinit) {
282
            this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'a[data-action]', this);
283
            this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'input[name="bcat[]"]', this);
284
            this.get('categorylisting').delegate('change', this.handleBulkSortByaction, '#menuselectsortby', this);
285
            this.categoriesinit = true;
286
        } else {
287
        }
288
    },
289
 
290
    /**
291
     * Initialises all the categories being shown.
292
     * @method initialiseCourses
293
     * @private
294
     * @return {boolean}
295
     */
296
    initialiseCourses: function() {
297
        var category = this.getCategoryById(this.get('activecategoryid')),
298
            listing = this.get('courselisting'),
299
            count = 0;
300
        if (!listing) {
301
            return false;
302
        }
303
 
304
        // Disable course move to bulk action as nothing will be selected on initialise.
305
        var menumovecoursesto = listing.one('#menumovecoursesto');
306
        if (menumovecoursesto) {
307
            menumovecoursesto.setAttribute('disabled', true);
308
        }
309
 
310
        listing.all('.listitem[data-id]').each(function(node) {
311
            this.registerCourse(new Course({
312
                node: node,
313
                console: this,
314
                category: category
315
            }));
316
            count++;
317
        }, this);
318
        listing.delegate('click', this.handleCourseDelegation, 'a[data-action]', this);
319
        listing.delegate('click', this.handleCourseDelegation, 'input[name="bc[]"]', this);
320
    },
321
 
322
    /**
323
     * Registers a course within the management display.
324
     * @method registerCourse
325
     * @param {Course} course
326
     */
327
    registerCourse: function(course) {
328
        var courses = this.get('courses');
329
        courses.push(course);
330
        this.set('courses', courses);
331
    },
332
 
333
    /**
334
     * Handles the event fired by a delegated course listener.
335
     *
336
     * @method handleCourseDelegation
337
     * @protected
338
     * @param {EventFacade} e
339
     */
340
    handleCourseDelegation: function(e) {
341
        var target = e.currentTarget,
342
            action = target.getData('action'),
343
            courseid = target.ancestor('.listitem').getData('id'),
344
            course = this.getCourseById(courseid);
345
        if (course) {
346
            course.handle(action, e);
347
        } else {
348
        }
349
    },
350
 
351
    /**
352
     * Handles the event fired by a delegated course listener.
353
     *
354
     * @method handleCategoryDelegation
355
     * @protected
356
     * @param {EventFacade} e
357
     */
358
    handleCategoryDelegation: function(e) {
359
        var target = e.currentTarget,
360
            action = target.getData('action'),
361
            categoryid = target.ancestor('.listitem').getData('id'),
362
            category = this.getCategoryById(categoryid);
363
        if (category) {
364
            category.handle(action, e);
365
        } else {
366
        }
367
    },
368
 
369
    /**
370
     * Check if any course is selected.
371
     *
372
     * @method isCourseSelected
373
     * @param {Node} checkboxnode Checkbox node on which action happened.
374
     * @return bool
375
     */
376
    isCourseSelected: function(checkboxnode) {
377
        var selected = false;
378
 
379
        // If any course selected then show move to category select box.
380
        if (checkboxnode && checkboxnode.get('checked')) {
381
            selected = true;
382
        } else {
383
            var i,
384
                course,
385
                courses = this.get('courses'),
386
                length = courses.length;
387
            for (i = 0; i < length; i++) {
388
                if (courses.hasOwnProperty(i)) {
389
                    course = courses[i];
390
                    if (course.get('node').one('input[name="bc[]"]').get('checked')) {
391
                        selected = true;
392
                        break;
393
                    }
394
                }
395
            }
396
        }
397
        return selected;
398
    },
399
 
400
    /**
401
     * Check if any category is selected.
402
     *
403
     * @method isCategorySelected
404
     * @param {Node} checkboxnode Checkbox node on which action happened.
405
     * @return bool
406
     */
407
    isCategorySelected: function(checkboxnode) {
408
        var selected = false;
409
 
410
        // If any category selected then show move to category select box.
411
        if (checkboxnode && checkboxnode.get('checked')) {
412
            selected = true;
413
        } else {
414
            var i,
415
                category,
416
                categories = this.get('categories'),
417
                length = categories.length;
418
            for (i = 0; i < length; i++) {
419
                if (categories.hasOwnProperty(i)) {
420
                    category = categories[i];
421
                    if (category.get('node').one('input[name="bcat[]"]').get('checked')) {
422
                        selected = true;
423
                        break;
424
                    }
425
                }
426
            }
427
        }
428
        return selected;
429
    },
430
 
431
    /**
432
     * Handle bulk sort action.
433
     *
434
     * @method handleBulkSortByaction
435
     * @protected
436
     * @param {EventFacade} e
437
     */
438
    handleBulkSortByaction: function(e) {
439
        var sortcategoryby = this.get('categorylisting').one('#menuresortcategoriesby'),
440
            sortcourseby = this.get('categorylisting').one('#menuresortcoursesby'),
441
            sortbybutton = this.get('categorylisting').one('input[name="bulksort"]'),
442
            sortby = e;
443
 
444
        if (!sortby) {
445
            sortby = this.get('categorylisting').one('#menuselectsortby');
446
        } else {
447
            if (e && e.currentTarget) {
448
                sortby = e.currentTarget;
449
            }
450
        }
451
 
452
        // If no sortby select found then return as we can't do anything.
453
        if (!sortby) {
454
            return;
455
        }
456
 
457
        if ((this.get('categories').length <= 1) || (!this.isCategorySelected() &&
458
                (sortby.get("options").item(sortby.get('selectedIndex')).getAttribute('value') === 'selectedcategories'))) {
459
            if (sortcategoryby) {
460
                sortcategoryby.setAttribute('disabled', true);
461
            }
462
            if (sortcourseby) {
463
                sortcourseby.setAttribute('disabled', true);
464
            }
465
            if (sortbybutton) {
466
                sortbybutton.setAttribute('disabled', true);
467
            }
468
        } else {
469
            if (sortcategoryby) {
470
                sortcategoryby.removeAttribute('disabled');
471
            }
472
            if (sortcourseby) {
473
                sortcourseby.removeAttribute('disabled');
474
            }
475
            if (sortbybutton) {
476
                sortbybutton.removeAttribute('disabled');
477
            }
478
        }
479
    },
480
 
481
    /**
482
     * Returns the category with the given ID.
483
     * @method getCategoryById
484
     * @param {Number} id
485
     * @return {Category|Boolean} The category or false if it can't be found.
486
     */
487
    getCategoryById: function(id) {
488
        var i,
489
            category,
490
            categories = this.get('categories'),
491
            length = categories.length;
492
        for (i = 0; i < length; i++) {
493
            if (categories.hasOwnProperty(i)) {
494
                category = categories[i];
495
                if (category.get('categoryid') === id) {
496
                    return category;
497
                }
498
            }
499
        }
500
        return false;
501
    },
502
 
503
    /**
504
     * Returns the course with the given id.
505
     * @method getCourseById
506
     * @param {Number} id
507
     * @return {Course|Boolean} The course or false if not found/
508
     */
509
    getCourseById: function(id) {
510
        var i,
511
            course,
512
            courses = this.get('courses'),
513
            length = courses.length;
514
        for (i = 0; i < length; i++) {
515
            if (courses.hasOwnProperty(i)) {
516
                course = courses[i];
517
                if (course.get('courseid') === id) {
518
                    return course;
519
                }
520
            }
521
        }
522
        return false;
523
    },
524
 
525
    /**
526
     * Removes the course with the given ID.
527
     * @method removeCourseById
528
     * @param {Number} id
529
     */
530
    removeCourseById: function(id) {
531
        var courses = this.get('courses'),
532
            length = courses.length,
533
            course,
534
            i;
535
        for (i = 0; i < length; i++) {
536
            course = courses[i];
537
            if (course.get('courseid') === id) {
538
                courses.splice(i, 1);
539
                break;
540
            }
541
        }
542
    },
543
 
544
    /**
545
     * Performs an AJAX action.
546
     *
547
     * @method performAjaxAction
548
     * @param {String} action The action to perform.
549
     * @param {Object} args The arguments to pass through with teh request.
550
     * @param {Function} callback The function to call when all is done.
551
     * @param {Object} context The object to use as the context for the callback.
552
     */
553
    performAjaxAction: function(action, args, callback, context) {
554
        var io = new Y.IO();
555
        args.action = action;
556
        args.ajax = '1';
557
        args.sesskey = M.cfg.sesskey;
558
        if (callback === null) {
559
            callback = function() {
560
            };
561
        }
562
        io.send(this.get('ajaxurl'), {
563
            method: 'POST',
564
            on: {
565
                complete: callback
566
            },
567
            context: context,
568
            data: args,
569
            'arguments': args
570
        });
571
    }
572
};
573
Y.extend(Console, Y.Base, Console.prototype);
574
 
575
M.course = M.course || {};
576
M.course.management = M.course.management || {};
577
M.course.management.console = null;
578
 
579
/**
580
 * Initalises the course management console.
581
 *
582
 * @method M.course.management.init
583
 * @static
584
 * @param {Object} config
585
 */
586
M.course.management.init = function(config) {
587
    M.course.management.console = new Console(config);
588
};
589
/**
590
 * Drag and Drop handler
591
 *
592
 * @namespace M.course.management
593
 * @class DragDrop
594
 * @constructor
595
 * @extends Base
596
 */
597
DragDrop = function(config) {
598
    Console.superclass.constructor.apply(this, [config]);
599
};
600
DragDrop.NAME = 'moodle-course-management-dd';
601
DragDrop.CSS_PREFIX = 'management-dd';
602
DragDrop.ATTRS = {
603
    /**
604
     * The management console this drag and drop has been set up for.
605
     * @attribute console
606
     * @type Console
607
     * @writeOnce
608
     */
609
    console: {
610
        writeOnce: 'initOnly'
611
    }
612
};
613
DragDrop.prototype = {
614
    /**
615
     * True if the user is dragging a course upwards.
616
     * @property goingup
617
     * @protected
618
     * @default false
619
     */
620
    goingup: false,
621
 
622
    /**
623
     * The last Y position of the course being dragged
624
     * @property lasty
625
     * @protected
626
     * @default null
627
     */
628
    lasty: null,
629
 
630
    /**
631
     * The sibling above the course being dragged currently (tracking its original position).
632
     *
633
     * @property previoussibling
634
     * @protected
635
     * @default false
636
     */
637
    previoussibling: null,
638
 
639
    /**
640
     * Initialises the DragDrop instance.
641
     * @method initializer
642
     */
643
    initializer: function() {
644
        var managementconsole = this.get('console'),
645
            container = managementconsole.get('element'),
646
            categorylisting = container.one('#category-listing'),
647
            courselisting = container.one('#course-listing > .course-listing'),
648
            categoryul = (categorylisting) ? categorylisting.one('ul.ml') : null,
649
            courseul = (courselisting) ? courselisting.one('ul.ml') : null,
650
            canmoveoutof = (courselisting) ? courselisting.getData('canmoveoutof') : false,
651
            contstraint = (canmoveoutof) ? container : courseul;
652
 
653
        if (!courseul) {
654
            // No course listings found.
655
            return false;
656
        }
657
 
658
        while (contstraint.get('scrollHeight') === 0 && !contstraint.compareTo(window.document.body)) {
659
            contstraint = contstraint.get('parentNode');
660
        }
661
 
662
        courseul.all('> li').each(function(li) {
663
            this.initCourseListing(li, contstraint);
664
        }, this);
665
        courseul.setData('dd', new Y.DD.Drop({
666
            node: courseul
667
        }));
668
        if (canmoveoutof && categoryul) {
669
            // Category UL may not be there if viewmode is just courses.
670
            categoryul.all('li > div').each(function(div) {
671
                this.initCategoryListitem(div);
672
            }, this);
673
        }
674
        Y.DD.DDM.on('drag:start', this.dragStart, this);
675
        Y.DD.DDM.on('drag:end', this.dragEnd, this);
676
        Y.DD.DDM.on('drag:drag', this.dragDrag, this);
677
        Y.DD.DDM.on('drop:over', this.dropOver, this);
678
        Y.DD.DDM.on('drop:enter', this.dropEnter, this);
679
        Y.DD.DDM.on('drop:exit', this.dropExit, this);
680
        Y.DD.DDM.on('drop:hit', this.dropHit, this);
681
 
682
    },
683
 
684
    /**
685
     * Initialises a course listing.
686
     * @method initCourseListing
687
     * @param Node
688
     */
689
    initCourseListing: function(node, contstraint) {
690
        node.setData('dd', new Y.DD.Drag({
691
            node: node,
692
            target: {
693
                padding: '0 0 0 20'
694
            }
695
        }).addHandle(
696
            '.drag-handle'
697
        ).plug(Y.Plugin.DDProxy, {
698
            moveOnEnd: false,
699
            borderStyle: false
700
        }).plug(Y.Plugin.DDConstrained, {
701
            constrain2node: contstraint
702
        }));
703
    },
704
 
705
    /**
706
     * Initialises a category listing.
707
     * @method initCategoryListitem
708
     * @param Node
709
     */
710
    initCategoryListitem: function(node) {
711
        node.setData('dd', new Y.DD.Drop({
712
            node: node
713
        }));
714
    },
715
 
716
    /**
717
     * Dragging has started.
718
     * @method dragStart
719
     * @private
720
     * @param {EventFacade} e
721
     */
722
    dragStart: function(e) {
723
        var drag = e.target,
724
            node = drag.get('node'),
725
            dragnode = drag.get('dragNode');
726
        node.addClass('course-being-dragged');
727
        dragnode.addClass('course-being-dragged-proxy').set('innerHTML', node.one('a.coursename').get('innerHTML'));
728
        this.previoussibling = node.get('previousSibling');
729
    },
730
 
731
    /**
732
     * Dragging has ended.
733
     * @method dragEnd
734
     * @private
735
     * @param {EventFacade} e
736
     */
737
    dragEnd: function(e) {
738
        var drag = e.target,
739
            node = drag.get('node');
740
        node.removeClass('course-being-dragged');
741
        this.get('console').get('element').all('#category-listing li.highlight').removeClass('highlight');
742
    },
743
 
744
    /**
745
     * Dragging in progress.
746
     * @method dragDrag
747
     * @private
748
     * @param {EventFacade} e
749
     */
750
    dragDrag: function(e) {
751
        var y = e.target.lastXY[1];
752
        if (y < this.lasty) {
753
            this.goingup = true;
754
        } else {
755
            this.goingup = false;
756
        }
757
        this.lasty = y;
758
    },
759
 
760
    /**
761
     * The course has been dragged over a drop target.
762
     * @method dropOver
763
     * @private
764
     * @param {EventFacade} e
765
     */
766
    dropOver: function(e) {
767
        // Get a reference to our drag and drop nodes
768
        var drag = e.drag.get('node'),
769
            drop = e.drop.get('node'),
770
            tag = drop.get('tagName').toLowerCase();
771
        if (tag === 'li' && drop.hasClass('listitem-course')) {
772
            if (!this.goingup) {
773
                drop = drop.get('nextSibling');
774
                if (!drop) {
775
                    drop = e.drop.get('node');
776
                    drop.get('parentNode').append(drag);
777
                    return false;
778
                }
779
            }
780
            drop.get('parentNode').insertBefore(drag, drop);
781
            e.drop.sizeShim();
782
        }
783
    },
784
 
785
    /**
786
     * The course has been dragged over a drop target.
787
     * @method dropEnter
788
     * @private
789
     * @param {EventFacade} e
790
     */
791
    dropEnter: function(e) {
792
        var drop = e.drop.get('node'),
793
            tag = drop.get('tagName').toLowerCase();
794
        if (tag === 'div') {
795
            drop.ancestor('li.listitem-category').addClass('highlight');
796
        }
797
    },
798
 
799
    /**
800
     * The course has been dragged off a drop target.
801
     * @method dropExit
802
     * @private
803
     * @param {EventFacade} e
804
     */
805
    dropExit: function(e) {
806
        var drop = e.drop.get('node'),
807
            tag = drop.get('tagName').toLowerCase();
808
        if (tag === 'div') {
809
            drop.ancestor('li.listitem-category').removeClass('highlight');
810
        }
811
    },
812
 
813
    /**
814
     * The course has been dropped on a target.
815
     * @method dropHit
816
     * @private
817
     * @param {EventFacade} e
818
     */
819
    dropHit: function(e) {
820
        var drag = e.drag.get('node'),
821
            drop = e.drop.get('node'),
822
            iscategory = (drop.ancestor('.listitem-category') !== null),
823
            iscourse = !iscategory && (drop.test('.listitem-course')),
824
            managementconsole = this.get('console'),
825
            categoryid,
826
            category,
827
            courseid,
828
            course,
829
            aftercourseid,
830
            previoussibling,
831
            previousid;
832
 
833
        if (!drag.test('.listitem-course')) {
834
            return false;
835
        }
836
        courseid = drag.getData('id');
837
        if (iscategory) {
838
            categoryid = drop.ancestor('.listitem-category').getData('id');
839
            category = managementconsole.getCategoryById(categoryid);
840
            if (category) {
841
                course = managementconsole.getCourseById(courseid);
842
                if (course) {
843
                    category.moveCourseTo(course);
844
                }
845
            }
846
        } else if (iscourse || drop.ancestor('#course-listing')) {
847
            course = managementconsole.getCourseById(courseid);
848
            previoussibling = drag.get('previousSibling');
849
            aftercourseid = (previoussibling) ? previoussibling.getData('id') || 0 : 0;
850
            previousid = (this.previoussibling) ? this.previoussibling.getData('id') : 0;
851
            if (aftercourseid !== previousid) {
852
                course.moveAfter(aftercourseid, previousid);
853
            }
854
        } else {
855
        }
856
    }
857
};
858
Y.extend(DragDrop, Y.Base, DragDrop.prototype);
859
/**
860
 * A managed course.
861
 *
862
 * @namespace M.course.management
863
 * @class Item
864
 * @constructor
865
 * @extends Base
866
 */
867
Item = function() {
868
    Item.superclass.constructor.apply(this, arguments);
869
};
870
Item.NAME = 'moodle-course-management-item';
871
Item.CSS_PREFIX = 'management-item';
872
Item.ATTRS = {
873
    /**
874
     * The node for this item.
875
     * @attribute node
876
     * @type Node
877
     */
878
    node: {},
879
 
880
    /**
881
     * The management console.
882
     * @attribute console
883
     * @type Console
884
     */
885
    console: {},
886
 
887
    /**
888
     * Describes the type of this item. Should be set by the extending class.
889
     * @attribute itemname
890
     * @type {String}
891
     * @default item
892
     */
893
    itemname: {
894
        value: 'item'
895
    }
896
};
897
Item.prototype = {
898
    /**
899
     * The highlight timeout for this item if there is one.
900
     * @property highlighttimeout
901
     * @protected
902
     * @type Timeout
903
     * @default null
904
     */
905
    highlighttimeout: null,
906
 
907
    /**
908
     * Checks and parses an AJAX response for an item.
909
     *
910
     * @method checkAjaxResponse
911
     * @protected
912
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
913
     * @param {Object} response The response from the AJAX request.
914
     * @param {Object} args The arguments given to the request.
915
     * @return {Object|Boolean}
916
     */
917
    checkAjaxResponse: function(transactionid, response, args) {
918
        if (response.status !== 200) {
919
            return false;
920
        }
921
        if (transactionid === null || args === null) {
922
            return false;
923
        }
924
        var outcome = Y.JSON.parse(response.responseText);
925
        if (outcome.error !== false) {
926
            new M.core.exception(outcome);
927
        }
928
        if (outcome.outcome === false) {
929
            return false;
930
        }
931
        return outcome;
932
    },
933
 
934
    /**
935
     * Moves an item up by one.
936
     *
937
     * @method moveup
938
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
939
     * @param {Object} response The response from the AJAX request.
940
     * @param {Object} args The arguments given to the request.
941
     * @return {Boolean}
942
     */
943
    moveup: function(transactionid, response, args) {
944
        var node,
945
            nodeup,
946
            nodedown,
947
            previous,
948
            previousup,
949
            previousdown,
950
            tmpnode,
951
            outcome = this.checkAjaxResponse(transactionid, response, args);
952
        if (outcome === false) {
953
            return false;
954
        }
955
        node = this.get('node');
956
        previous = node.previous('.listitem');
957
        if (previous) {
958
            previous.insert(node, 'before');
959
            previousup = previous.one(' > div a.action-moveup');
960
            nodedown = node.one(' > div a.action-movedown');
961
            if (!previousup || !nodedown) {
962
                // We can have two situations here:
963
                //   1. previousup is not set and nodedown is not set. This happens when there are only two courses.
964
                //   2. nodedown is not set. This happens when they are moving the bottom course up.
965
                // node up and previous down should always be there. They would be required to trigger the action.
966
                nodeup = node.one(' > div a.action-moveup');
967
                previousdown = previous.one(' > div a.action-movedown');
968
                if (!previousup && !nodedown) {
969
                    // Ok, must be two courses. We need to switch the up and down icons.
970
                    tmpnode = Y.Node.create('<a style="visibility:hidden;">&nbsp;</a>');
971
                    previousdown.replace(tmpnode);
972
                    nodeup.replace(previousdown);
973
                    tmpnode.replace(nodeup);
974
                    tmpnode.destroy();
975
                } else if (!nodedown) {
976
                    // previous down needs to be given to node.
977
                    nodeup.insert(previousdown, 'after');
978
                }
979
            }
980
            nodeup = node.one(' > div a.action-moveup');
981
            if (nodeup) {
982
                // Try to re-focus on up.
983
                nodeup.focus();
984
            } else {
985
                // If we can't focus up we're at the bottom, try to focus on up.
986
                nodedown = node.one(' > div a.action-movedown');
987
                if (nodedown) {
988
                    nodedown.focus();
989
                }
990
            }
991
            this.updated(true);
992
        } else {
993
            // Aha it succeeded but this is the top item in the list. Pagination is in play!
994
            // Refresh to update the state of things.
995
            window.location.reload();
996
        }
997
    },
998
 
999
    /**
1000
     * Moves an item down by one.
1001
     *
1002
     * @method movedown
1003
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1004
     * @param {Object} response The response from the AJAX request.
1005
     * @param {Object} args The arguments given to the request.
1006
     * @return {Boolean}
1007
     */
1008
    movedown: function(transactionid, response, args) {
1009
        var node,
1010
            next,
1011
            nodeup,
1012
            nodedown,
1013
            nextup,
1014
            nextdown,
1015
            tmpnode,
1016
            outcome = this.checkAjaxResponse(transactionid, response, args);
1017
        if (outcome === false) {
1018
            return false;
1019
        }
1020
        node = this.get('node');
1021
        next = node.next('.listitem');
1022
        if (next) {
1023
            node.insert(next, 'before');
1024
            nextdown = next.one(' > div a.action-movedown');
1025
            nodeup = node.one(' > div a.action-moveup');
1026
            if (!nextdown || !nodeup) {
1027
                // next up and node down should always be there. They would be required to trigger the action.
1028
                nextup = next.one(' > div a.action-moveup');
1029
                nodedown = node.one(' > div a.action-movedown');
1030
                if (!nextdown && !nodeup) {
1031
                    // We can have two situations here:
1032
                    //   1. nextdown is not set and nodeup is not set. This happens when there are only two courses.
1033
                    //   2. nodeup is not set. This happens when we are moving the first course down.
1034
                    // Ok, must be two courses. We need to switch the up and down icons.
1035
                    tmpnode = Y.Node.create('<a style="visibility:hidden;">&nbsp;</a>');
1036
                    nextup.replace(tmpnode);
1037
                    nodedown.replace(nextup);
1038
                    tmpnode.replace(nodedown);
1039
                    tmpnode.destroy();
1040
                } else if (!nodeup) {
1041
                    // next up needs to be given to node.
1042
                    nodedown.insert(nextup, 'before');
1043
                }
1044
            }
1045
            nodedown = node.one(' > div a.action-movedown');
1046
            if (nodedown) {
1047
                // Try to ensure the up is focused again.
1048
                nodedown.focus();
1049
            } else {
1050
                // If we can't focus up we're at the top, try to focus on down.
1051
                nodeup = node.one(' > div a.action-moveup');
1052
                if (nodeup) {
1053
                    nodeup.focus();
1054
                }
1055
            }
1056
            this.updated(true);
1057
        } else {
1058
            // Aha it succeeded but this is the bottom item in the list. Pagination is in play!
1059
            // Refresh to update the state of things.
1060
            window.location.reload();
1061
        }
1062
    },
1063
 
1064
    /**
1065
     * Makes an item visible.
1066
     *
1067
     * @method show
1068
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1069
     * @param {Object} response The response from the AJAX request.
1070
     * @param {Object} args The arguments given to the request.
1071
     * @return {Boolean}
1072
     */
1073
    show: function(transactionid, response, args) {
1074
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1075
            hidebtn;
1076
        if (outcome === false) {
1077
            return false;
1078
        }
1079
 
1080
        this.markVisible();
1081
        hidebtn = this.get('node').one('a[data-action=hide]');
1082
        if (hidebtn) {
1083
            hidebtn.focus();
1084
        }
1085
        this.updated();
1086
    },
1087
 
1088
    /**
1089
     * Marks the item as visible
1090
     * @method markVisible
1091
     */
1092
    markVisible: function() {
1093
        this.get('node').setAttribute('data-visible', '1');
1094
        return true;
1095
    },
1096
 
1097
    /**
1098
     * Hides an item.
1099
     *
1100
     * @method hide
1101
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1102
     * @param {Object} response The response from the AJAX request.
1103
     * @param {Object} args The arguments given to the request.
1104
     * @return {Boolean}
1105
     */
1106
    hide: function(transactionid, response, args) {
1107
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1108
            showbtn;
1109
        if (outcome === false) {
1110
            return false;
1111
        }
1112
        this.markHidden();
1113
        showbtn = this.get('node').one('a[data-action=show]');
1114
        if (showbtn) {
1115
            showbtn.focus();
1116
        }
1117
        this.updated();
1118
    },
1119
 
1120
    /**
1121
     * Marks the item as hidden.
1122
     * @method makeHidden
1123
     */
1124
    markHidden: function() {
1125
        this.get('node').setAttribute('data-visible', '0');
1126
        return true;
1127
    },
1128
 
1129
    /**
1130
     * Called when ever a node is updated.
1131
     *
1132
     * @method updated
1133
     * @param {Boolean} moved True if this item was moved.
1134
     */
1135
    updated: function(moved) {
1136
        if (moved) {
1137
            this.highlight();
1138
        }
1139
    },
1140
 
1141
    /**
1142
     * Highlights this option for a breif time.
1143
     *
1144
     * @method highlight
1145
     */
1146
    highlight: function() {
1147
        var node = this.get('node');
1148
        node.siblings('.highlight').removeClass('highlight');
1149
        node.addClass('highlight');
1150
        if (this.highlighttimeout) {
1151
            window.clearTimeout(this.highlighttimeout);
1152
        }
1153
        this.highlighttimeout = window.setTimeout(function() {
1154
            node.removeClass('highlight');
1155
        }, 2500);
1156
    }
1157
};
1158
Y.extend(Item, Y.Base, Item.prototype);
1159
/**
1160
 * A managed category.
1161
 *
1162
 * @namespace M.course.management
1163
 * @class Category
1164
 * @constructor
1165
 * @extends Item
1166
 */
1167
Category = function() {
1168
    Category.superclass.constructor.apply(this, arguments);
1169
};
1170
Category.NAME = 'moodle-course-management-category';
1171
Category.CSS_PREFIX = 'management-category';
1172
Category.ATTRS = {
1173
    /**
1174
     * The category ID relating to this category.
1175
     * @attribute categoryid
1176
     * @type Number
1177
     * @writeOnce
1178
     * @default null
1179
     */
1180
    categoryid: {
1181
        getter: function(value, name) {
1182
            if (value === null) {
1183
                value = this.get('node').getData('id');
1184
                this.set(name, value);
1185
            }
1186
            return value;
1187
        },
1188
        value: null,
1189
        writeOnce: true
1190
    },
1191
 
1192
    /**
1193
     * True if this category is the currently selected category.
1194
     * @attribute selected
1195
     * @type Boolean
1196
     * @default null
1197
     */
1198
    selected: {
1199
        getter: function(value, name) {
1200
            if (value === null) {
1201
                value = this.get('node').getData(name);
1202
                if (value === null) {
1203
                    value = false;
1204
                }
1205
                this.set(name, value);
1206
            }
1207
            return value;
1208
        },
1209
        value: null
1210
    },
1211
 
1212
    /**
1213
     * An array of courses belonging to this category.
1214
     * @attribute courses
1215
     * @type Course[]
1216
     * @default Array
1217
     */
1218
    courses: {
1219
        validator: function(val) {
1220
            return Y.Lang.isArray(val);
1221
        },
1222
        value: []
1223
    }
1224
};
1225
Category.prototype = {
1226
    /**
1227
     * Initialises an instance of a Category.
1228
     * @method initializer
1229
     */
1230
    initializer: function() {
1231
        this.set('itemname', 'category');
1232
    },
1233
 
1234
    /**
1235
     * Returns the name of the category.
1236
     * @method getName
1237
     * @return {String}
1238
     */
1239
    getName: function() {
1240
        return this.get('node').one('a.categoryname').get('innerHTML');
1241
    },
1242
 
1243
    /**
1244
     * Registers a course as belonging to this category.
1245
     * @method registerCourse
1246
     * @param {Course} course
1247
     */
1248
    registerCourse: function(course) {
1249
        var courses = this.get('courses');
1250
        courses.push(course);
1251
        this.set('courses', courses);
1252
    },
1253
 
1254
    /**
1255
     * Handles a category related event.
1256
     *
1257
     * @method handle
1258
     * @param {String} action
1259
     * @param {EventFacade} e
1260
     * @return {Boolean}
1261
     */
1262
    handle: function(action, e) {
1263
        var catarg = {categoryid: this.get('categoryid')},
1264
            selected = this.get('console').get('activecategoryid');
1265
        if (selected && selected !== catarg.categoryid) {
1266
            catarg.selectedcategory = selected;
1267
        }
1268
        switch (action) {
1269
            case 'moveup':
1270
                e.preventDefault();
1271
                this.get('console').performAjaxAction('movecategoryup', catarg, this.moveup, this);
1272
                break;
1273
            case 'movedown':
1274
                e.preventDefault();
1275
                this.get('console').performAjaxAction('movecategorydown', catarg, this.movedown, this);
1276
                break;
1277
            case 'show':
1278
                e.preventDefault();
1279
                this.get('console').performAjaxAction('showcategory', catarg, this.show, this);
1280
                break;
1281
            case 'hide':
1282
                e.preventDefault();
1283
                this.get('console').performAjaxAction('hidecategory', catarg, this.hide, this);
1284
                break;
1285
            case 'expand':
1286
                e.preventDefault();
1287
                if (this.get('node').getData('expanded') === '0') {
1288
                    this.get('node').setAttribute('data-expanded', '1').setData('expanded', 'true');
1289
                    this.get('console').performAjaxAction('getsubcategorieshtml', catarg, this.loadSubcategories, this);
1290
                }
1291
                this.expand();
1292
                break;
1293
            case 'collapse':
1294
                e.preventDefault();
1295
                this.collapse();
1296
                break;
1297
            case 'select':
1298
                var c = this.get('console'),
1299
                    movecategoryto = c.get('categorylisting').one('#menumovecategoriesto');
1300
                // If any category is selected and there are more then one categories.
1301
                if (movecategoryto) {
1302
                    if (c.isCategorySelected(e.currentTarget) &&
1303
                            c.get('categories').length > 1) {
1304
                        movecategoryto.removeAttribute('disabled');
1305
                    } else {
1306
                        movecategoryto.setAttribute('disabled', true);
1307
                    }
1308
                    c.handleBulkSortByaction();
1309
                }
1310
                break;
1311
            default:
1312
                return false;
1313
        }
1314
    },
1315
 
1316
    /**
1317
     * Expands the category making its sub categories visible.
1318
     * @method expand
1319
     */
1320
    expand: function() {
1321
        var node = this.get('node'),
1322
            action = node.one('a[data-action=expand]'),
1323
            ul = node.one('ul[role=group]');
1324
        node.removeClass('collapsed').setAttribute('aria-expanded', 'true');
1325
        action.setAttribute('data-action', 'collapse').setAttrs({
1326
            title: M.util.get_string('collapsecategory', 'moodle', this.getName())
1327
        });
1328
 
1329
        require(['core/str', 'core/templates', 'core/notification'], function(Str, Templates, Notification) {
1330
            Str.get_string('collapse', 'core')
1331
                .then(function(string) {
1332
                    return Templates.renderPix('t/switch_minus', 'core', string);
1333
                })
1334
                .then(function(html) {
1335
                    html = Y.Node.create(html).addClass('tree-icon').getDOMNode().outerHTML;
1336
                    return action.set('innerHTML', html);
1337
                }).fail(Notification.exception);
1338
        });
1339
 
1340
        if (ul) {
1341
            ul.setAttribute('aria-hidden', 'false');
1342
        }
1343
        this.get('console').performAjaxAction('expandcategory', {categoryid: this.get('categoryid')}, null, this);
1344
    },
1345
 
1346
    /**
1347
     * Collapses the category making its sub categories hidden.
1348
     * @method collapse
1349
     */
1350
    collapse: function() {
1351
        var node = this.get('node'),
1352
            action = node.one('a[data-action=collapse]'),
1353
            ul = node.one('ul[role=group]');
1354
        node.addClass('collapsed').setAttribute('aria-expanded', 'false');
1355
        action.setAttribute('data-action', 'expand').setAttrs({
1356
            title: M.util.get_string('expandcategory', 'moodle', this.getName())
1357
        });
1358
 
1359
        require(['core/str', 'core/templates', 'core/notification'], function(Str, Templates, Notification) {
1360
            Str.get_string('expand', 'core')
1361
                .then(function(string) {
1362
                    return Templates.renderPix('t/switch_plus', 'core', string);
1363
                })
1364
                .then(function(html) {
1365
                    html = Y.Node.create(html).addClass('tree-icon').getDOMNode().outerHTML;
1366
                    return action.set('innerHTML', html);
1367
                }).fail(Notification.exception);
1368
        });
1369
 
1370
        if (ul) {
1371
            ul.setAttribute('aria-hidden', 'true');
1372
        }
1373
        this.get('console').performAjaxAction('collapsecategory', {categoryid: this.get('categoryid')}, null, this);
1374
    },
1375
 
1376
    /**
1377
     * Loads sub categories provided by an AJAX request..
1378
     *
1379
     * @method loadSubcategories
1380
     * @protected
1381
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1382
     * @param {Object} response The response from the AJAX request.
1383
     * @param {Object} args The arguments given to the request.
1384
     * @return {Boolean} Returns true on success - false otherwise.
1385
     */
1386
    loadSubcategories: function(transactionid, response, args) {
1387
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1388
            node = this.get('node'),
1389
            managementconsole = this.get('console'),
1390
            ul,
1391
            actionnode;
1392
        if (outcome === false) {
1393
            return false;
1394
        }
1395
        node.append(outcome.html);
1396
        managementconsole.initialiseCategories(node);
1397
        if (M.core && M.core.actionmenu && M.core.actionmenu.newDOMNode) {
1398
            M.core.actionmenu.newDOMNode(node);
1399
        }
1400
        ul = node.one('ul[role=group]');
1401
        actionnode = node.one('a[data-action=collapse]');
1402
        if (ul && actionnode) {
1403
            actionnode.setAttribute('aria-controls', ul.generateID());
1404
        }
1405
        return true;
1406
    },
1407
 
1408
    /**
1409
     * Moves the course to this category.
1410
     *
1411
     * @method moveCourseTo
1412
     * @param {Course} course
1413
     */
1414
    moveCourseTo: function(course) {
1415
        require(['core/notification'], function(Notification) {
1416
            Notification.saveCancelPromise(
1417
                M.util.get_string('confirmation', 'admin'),
1418
                M.util.get_string('confirmcoursemove', 'moodle',
1419
                {
1420
                    course: course.getName(),
1421
                    category: this.getName(),
1422
                }),
1423
                M.util.get_string('move', 'moodle')
1424
            ).then(function() {
1425
                this.get('console').performAjaxAction('movecourseintocategory', {
1426
                    courseid: course.get('courseid'),
1427
                    categoryid: this.get('categoryid'),
1428
                }, this.completeMoveCourse, this);
1429
                return;
1430
            }.bind(this)).catch(function() {
1431
                // User cancelled.
1432
            });
1433
        }.bind(this));
1434
    },
1435
 
1436
    /**
1437
     * Completes moving a course to this category.
1438
     * @method completeMoveCourse
1439
     * @protected
1440
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1441
     * @param {Object} response The response from the AJAX request.
1442
     * @param {Object} args The arguments given to the request.
1443
     * @return {Boolean}
1444
     */
1445
    completeMoveCourse: function(transactionid, response, args) {
1446
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1447
            managementconsole = this.get('console'),
1448
            category,
1449
            course,
1450
            totals;
1451
        if (outcome === false) {
1452
            return false;
1453
        }
1454
        course = managementconsole.getCourseById(args.courseid);
1455
        if (!course) {
1456
            return false;
1457
        }
1458
        this.highlight();
1459
        if (course) {
1460
            if (outcome.paginationtotals) {
1461
                totals = managementconsole.get('courselisting').one('.listing-pagination-totals');
1462
                if (totals) {
1463
                    totals.set('innerHTML', outcome.paginationtotals);
1464
                }
1465
            }
1466
            if (outcome.totalcatcourses !== 'undefined') {
1467
                totals = this.get('node').one('.course-count span');
1468
                if (totals) {
1469
                    totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.totalcatcourses));
1470
                }
1471
            }
1472
            if (typeof outcome.fromcatcoursecount !== 'undefined') {
1473
                category = managementconsole.get('activecategoryid');
1474
                category = managementconsole.getCategoryById(category);
1475
                if (category) {
1476
                    totals = category.get('node').one('.course-count span');
1477
                    if (totals) {
1478
                        totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.fromcatcoursecount));
1479
                    }
1480
                }
1481
            }
1482
            course.remove();
1483
        }
1484
        return true;
1485
    },
1486
 
1487
    /**
1488
     * Makes an item visible.
1489
     *
1490
     * @method show
1491
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1492
     * @param {Object} response The response from the AJAX request.
1493
     * @param {Object} args The arguments given to the request.
1494
     * @return {Boolean}
1495
     */
1496
    show: function(transactionid, response, args) {
1497
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1498
            hidebtn;
1499
        if (outcome === false) {
1500
            return false;
1501
        }
1502
 
1503
        this.markVisible();
1504
        hidebtn = this.get('node').one('a[data-action=hide]');
1505
        if (hidebtn) {
1506
            hidebtn.focus();
1507
        }
1508
        if (outcome.categoryvisibility) {
1509
            this.updateChildVisibility(outcome.categoryvisibility);
1510
        }
1511
        if (outcome.coursevisibility) {
1512
            this.updateCourseVisiblity(outcome.coursevisibility);
1513
        }
1514
        this.updated();
1515
    },
1516
 
1517
    /**
1518
     * Hides an item.
1519
     *
1520
     * @method hide
1521
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1522
     * @param {Object} response The response from the AJAX request.
1523
     * @param {Object} args The arguments given to the request.
1524
     * @return {Boolean}
1525
     */
1526
    hide: function(transactionid, response, args) {
1527
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1528
            showbtn;
1529
        if (outcome === false) {
1530
            return false;
1531
        }
1532
        this.markHidden();
1533
        showbtn = this.get('node').one('a[data-action=show]');
1534
        if (showbtn) {
1535
            showbtn.focus();
1536
        }
1537
        if (outcome.categoryvisibility) {
1538
            this.updateChildVisibility(outcome.categoryvisibility);
1539
        }
1540
        if (outcome.coursevisibility) {
1541
            this.updateCourseVisiblity(outcome.coursevisibility);
1542
        }
1543
        this.updated();
1544
    },
1545
 
1546
    /**
1547
     * Updates the visibility of child courses if required.
1548
     * @method updateCourseVisiblity
1549
     * @chainable
1550
     * @param courses
1551
     */
1552
    updateCourseVisiblity: function(courses) {
1553
        var managementconsole = this.get('console'),
1554
            key,
1555
            course;
1556
        try {
1557
            for (key in courses) {
1558
                if (typeof courses[key] === 'object') {
1559
                    course = managementconsole.getCourseById(courses[key].id);
1560
                    if (course) {
1561
                        if (courses[key].visible === "1") {
1562
                            course.markVisible();
1563
                        } else {
1564
                            course.markHidden();
1565
                        }
1566
                    }
1567
                }
1568
            }
1569
        } catch (err) {
1570
        }
1571
        return this;
1572
    },
1573
 
1574
    /**
1575
     * Updates the visibility of subcategories if required.
1576
     * @method updateChildVisibility
1577
     * @chainable
1578
     * @param categories
1579
     */
1580
    updateChildVisibility: function(categories) {
1581
        var managementconsole = this.get('console'),
1582
            key,
1583
            category;
1584
        try {
1585
            for (key in categories) {
1586
                if (typeof categories[key] === 'object') {
1587
                    category = managementconsole.getCategoryById(categories[key].id);
1588
                    if (category) {
1589
                        if (categories[key].visible === "1") {
1590
                            category.markVisible();
1591
                        } else {
1592
                            category.markHidden();
1593
                        }
1594
                    }
1595
                }
1596
            }
1597
        } catch (err) {
1598
        }
1599
        return this;
1600
    }
1601
};
1602
Y.extend(Category, Item, Category.prototype);
1603
/**
1604
 * A managed course.
1605
 *
1606
 * @namespace M.course.management
1607
 * @class Course
1608
 * @constructor
1609
 * @extends Item
1610
 */
1611
Course = function() {
1612
    Course.superclass.constructor.apply(this, arguments);
1613
};
1614
Course.NAME = 'moodle-course-management-course';
1615
Course.CSS_PREFIX = 'management-course';
1616
Course.ATTRS = {
1617
 
1618
    /**
1619
     * The course ID of this course.
1620
     * @attribute courseid
1621
     * @type Number
1622
     */
1623
    courseid: {},
1624
 
1625
    /**
1626
     * True if this is the selected course.
1627
     * @attribute selected
1628
     * @type Boolean
1629
     * @default null
1630
     */
1631
    selected: {
1632
        getter: function(value, name) {
1633
            if (value === null) {
1634
                value = this.get('node').getData(name);
1635
                this.set(name, value);
1636
            }
1637
            return value;
1638
        },
1639
        value: null
1640
    },
1641
    node: {
1642
 
1643
    },
1644
    /**
1645
     * The management console tracking this course.
1646
     * @attribute console
1647
     * @type Console
1648
     * @writeOnce
1649
     */
1650
    console: {
1651
        writeOnce: 'initOnly'
1652
    },
1653
 
1654
    /**
1655
     * The category this course belongs to.
1656
     * @attribute category
1657
     * @type Category
1658
     * @writeOnce
1659
     */
1660
    category: {
1661
        writeOnce: 'initOnly'
1662
    }
1663
};
1664
Course.prototype = {
1665
    /**
1666
     * Initialises the new course instance.
1667
     * @method initializer
1668
     */
1669
    initializer: function() {
1670
        var node = this.get('node'),
1671
            category = this.get('category');
1672
        this.set('courseid', node.getData('id'));
1673
        if (category && category.registerCourse) {
1674
            category.registerCourse(this);
1675
        }
1676
        this.set('itemname', 'course');
1677
    },
1678
 
1679
    /**
1680
     * Returns the name of the course.
1681
     * @method getName
1682
     * @return {String}
1683
     */
1684
    getName: function() {
1685
        return this.get('node').one('a.coursename').get('innerHTML');
1686
    },
1687
 
1688
    /**
1689
     * Handles an event relating to this course.
1690
     * @method handle
1691
     * @param {String} action
1692
     * @param {EventFacade} e
1693
     * @return {Boolean}
1694
     */
1695
    handle: function(action, e) {
1696
        var managementconsole = this.get('console'),
1697
            args = {courseid: this.get('courseid')};
1698
        switch (action) {
1699
            case 'moveup':
1700
                e.halt();
1701
                managementconsole.performAjaxAction('movecourseup', args, this.moveup, this);
1702
                break;
1703
            case 'movedown':
1704
                e.halt();
1705
                managementconsole.performAjaxAction('movecoursedown', args, this.movedown, this);
1706
                break;
1707
            case 'show':
1708
                e.halt();
1709
                managementconsole.performAjaxAction('showcourse', args, this.show, this);
1710
                break;
1711
            case 'hide':
1712
                e.halt();
1713
                managementconsole.performAjaxAction('hidecourse', args, this.hide, this);
1714
                break;
1715
            case 'select':
1716
                var c = this.get('console'),
1717
                    movetonode = c.get('courselisting').one('#menumovecoursesto');
1718
                if (movetonode) {
1719
                    if (c.isCourseSelected(e.currentTarget)) {
1720
                        movetonode.removeAttribute('disabled');
1721
                    } else {
1722
                        movetonode.setAttribute('disabled', true);
1723
                    }
1724
                }
1725
                break;
1726
            default:
1727
                return false;
1728
        }
1729
    },
1730
 
1731
    /**
1732
     * Removes this course.
1733
     * @method remove
1734
     */
1735
    remove: function() {
1736
        this.get('console').removeCourseById(this.get('courseid'));
1737
        this.get('node').remove();
1738
    },
1739
 
1740
    /**
1741
     * Moves this course after another course.
1742
     *
1743
     * @method moveAfter
1744
     * @param {Number} moveaftercourse The course to move after or 0 to put it at the top.
1745
     * @param {Number} previousid the course it was previously after in case we need to revert.
1746
     */
1747
    moveAfter: function(moveaftercourse, previousid) {
1748
        var managementconsole = this.get('console'),
1749
            args = {
1750
                courseid: this.get('courseid'),
1751
                moveafter: moveaftercourse,
1752
                previous: previousid
1753
            };
1754
        managementconsole.performAjaxAction('movecourseafter', args, this.moveAfterResponse, this);
1755
    },
1756
 
1757
    /**
1758
     * Performs the actual move.
1759
     *
1760
     * @method moveAfterResponse
1761
     * @protected
1762
     * @param {Number} transactionid The transaction ID for the request.
1763
     * @param {Object} response The response to the request.
1764
     * @param {Objects} args The arguments that were given with the request.
1765
     * @return {Boolean}
1766
     */
1767
    moveAfterResponse: function(transactionid, response, args) {
1768
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1769
            node = this.get('node'),
1770
            previous;
1771
        if (outcome === false) {
1772
            previous = node.ancestor('ul').one('li[data-id=' + args.previous + ']');
1773
            if (previous) {
1774
                // After the last previous.
1775
                previous.insertAfter(node, 'after');
1776
            } else {
1777
                // Start of the list.
1778
                node.ancestor('ul').one('li').insert(node, 'before');
1779
            }
1780
            return false;
1781
        }
1782
        this.highlight();
1783
    }
1784
};
1785
Y.extend(Course, Item, Course.prototype);
1786
 
1787
 
1788
}, '@VERSION@', {
1789
    "requires": [
1790
        "base",
1791
        "node",
1792
        "io-base",
1793
        "moodle-core-notification-exception",
1794
        "json-parse",
1795
        "dd-constrain",
1796
        "dd-proxy",
1797
        "dd-drop",
1798
        "dd-delegate",
1799
        "node-event-delegate"
1800
    ]
1801
});