Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | 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'),
1441 ariadna 648
            categoryul = (categorylisting) ? categorylisting.one('ul.category-list') : null,
649
            courseul = (courselisting) ? courselisting.one('ul.course-list') : null,
1 efrain 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();
1441 ariadna 1283
                var courseCount = this.get('node').getData('course-count');
1284
                if (courseCount === '0') {
1285
                    this.get('console').performAjaxAction('hidecategory', catarg, this.hide, this);
1286
                    break;
1287
                }
1288
                var warningStr = courseCount === '1' ? 'hidecategoryone' : 'hidecategorymany';
1289
                var warningParams = {
1290
                    category: this.get('node').getData('category-name'),
1291
                    coursecount: courseCount,
1292
                };
1293
                require(['core/notification', 'core/str'], function(Notification, Str) {
1294
                    Notification.saveCancelPromise(
1295
                        Str.get_string('hidecategory'),
1296
                        Str.get_string(warningStr, 'core', warningParams),
1297
                        Str.get_string('hide')
1298
                    ).then(function() {
1299
                        this.get('console').performAjaxAction('hidecategory', catarg, this.hide, this);
1300
                    }.bind(this)
1301
                    ).catch(function() {
1302
                        // User cancelled, no action needed.
1303
                    });
1304
                }.bind(this));
1 efrain 1305
                break;
1306
            case 'expand':
1307
                e.preventDefault();
1308
                if (this.get('node').getData('expanded') === '0') {
1309
                    this.get('node').setAttribute('data-expanded', '1').setData('expanded', 'true');
1310
                    this.get('console').performAjaxAction('getsubcategorieshtml', catarg, this.loadSubcategories, this);
1311
                }
1312
                this.expand();
1313
                break;
1314
            case 'collapse':
1315
                e.preventDefault();
1316
                this.collapse();
1317
                break;
1318
            case 'select':
1319
                var c = this.get('console'),
1320
                    movecategoryto = c.get('categorylisting').one('#menumovecategoriesto');
1321
                // If any category is selected and there are more then one categories.
1322
                if (movecategoryto) {
1323
                    if (c.isCategorySelected(e.currentTarget) &&
1324
                            c.get('categories').length > 1) {
1325
                        movecategoryto.removeAttribute('disabled');
1326
                    } else {
1327
                        movecategoryto.setAttribute('disabled', true);
1328
                    }
1329
                    c.handleBulkSortByaction();
1330
                }
1331
                break;
1332
            default:
1333
                return false;
1334
        }
1335
    },
1336
 
1337
    /**
1338
     * Expands the category making its sub categories visible.
1339
     * @method expand
1340
     */
1341
    expand: function() {
1342
        var node = this.get('node'),
1343
            action = node.one('a[data-action=expand]'),
1344
            ul = node.one('ul[role=group]');
1345
        node.removeClass('collapsed').setAttribute('aria-expanded', 'true');
1346
        action.setAttribute('data-action', 'collapse').setAttrs({
1347
            title: M.util.get_string('collapsecategory', 'moodle', this.getName())
1348
        });
1349
 
1350
        require(['core/str', 'core/templates', 'core/notification'], function(Str, Templates, Notification) {
1351
            Str.get_string('collapse', 'core')
1352
                .then(function(string) {
1353
                    return Templates.renderPix('t/switch_minus', 'core', string);
1354
                })
1355
                .then(function(html) {
1356
                    html = Y.Node.create(html).addClass('tree-icon').getDOMNode().outerHTML;
1357
                    return action.set('innerHTML', html);
1358
                }).fail(Notification.exception);
1359
        });
1360
 
1361
        if (ul) {
1362
            ul.setAttribute('aria-hidden', 'false');
1363
        }
1364
        this.get('console').performAjaxAction('expandcategory', {categoryid: this.get('categoryid')}, null, this);
1365
    },
1366
 
1367
    /**
1368
     * Collapses the category making its sub categories hidden.
1369
     * @method collapse
1370
     */
1371
    collapse: function() {
1372
        var node = this.get('node'),
1373
            action = node.one('a[data-action=collapse]'),
1374
            ul = node.one('ul[role=group]');
1375
        node.addClass('collapsed').setAttribute('aria-expanded', 'false');
1376
        action.setAttribute('data-action', 'expand').setAttrs({
1377
            title: M.util.get_string('expandcategory', 'moodle', this.getName())
1378
        });
1379
 
1380
        require(['core/str', 'core/templates', 'core/notification'], function(Str, Templates, Notification) {
1381
            Str.get_string('expand', 'core')
1382
                .then(function(string) {
1383
                    return Templates.renderPix('t/switch_plus', 'core', string);
1384
                })
1385
                .then(function(html) {
1386
                    html = Y.Node.create(html).addClass('tree-icon').getDOMNode().outerHTML;
1387
                    return action.set('innerHTML', html);
1388
                }).fail(Notification.exception);
1389
        });
1390
 
1391
        if (ul) {
1392
            ul.setAttribute('aria-hidden', 'true');
1393
        }
1394
        this.get('console').performAjaxAction('collapsecategory', {categoryid: this.get('categoryid')}, null, this);
1395
    },
1396
 
1397
    /**
1398
     * Loads sub categories provided by an AJAX request..
1399
     *
1400
     * @method loadSubcategories
1401
     * @protected
1402
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1403
     * @param {Object} response The response from the AJAX request.
1404
     * @param {Object} args The arguments given to the request.
1405
     * @return {Boolean} Returns true on success - false otherwise.
1406
     */
1407
    loadSubcategories: function(transactionid, response, args) {
1408
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1409
            node = this.get('node'),
1410
            managementconsole = this.get('console'),
1411
            ul,
1412
            actionnode;
1413
        if (outcome === false) {
1414
            return false;
1415
        }
1416
        node.append(outcome.html);
1417
        managementconsole.initialiseCategories(node);
1418
        if (M.core && M.core.actionmenu && M.core.actionmenu.newDOMNode) {
1419
            M.core.actionmenu.newDOMNode(node);
1420
        }
1421
        ul = node.one('ul[role=group]');
1422
        actionnode = node.one('a[data-action=collapse]');
1423
        if (ul && actionnode) {
1424
            actionnode.setAttribute('aria-controls', ul.generateID());
1425
        }
1426
        return true;
1427
    },
1428
 
1429
    /**
1430
     * Moves the course to this category.
1431
     *
1432
     * @method moveCourseTo
1433
     * @param {Course} course
1434
     */
1435
    moveCourseTo: function(course) {
1436
        require(['core/notification'], function(Notification) {
1437
            Notification.saveCancelPromise(
1438
                M.util.get_string('confirmation', 'admin'),
1439
                M.util.get_string('confirmcoursemove', 'moodle',
1440
                {
1441
                    course: course.getName(),
1442
                    category: this.getName(),
1443
                }),
1444
                M.util.get_string('move', 'moodle')
1445
            ).then(function() {
1446
                this.get('console').performAjaxAction('movecourseintocategory', {
1447
                    courseid: course.get('courseid'),
1448
                    categoryid: this.get('categoryid'),
1449
                }, this.completeMoveCourse, this);
1450
                return;
1451
            }.bind(this)).catch(function() {
1452
                // User cancelled.
1453
            });
1454
        }.bind(this));
1455
    },
1456
 
1457
    /**
1458
     * Completes moving a course to this category.
1459
     * @method completeMoveCourse
1460
     * @protected
1461
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1462
     * @param {Object} response The response from the AJAX request.
1463
     * @param {Object} args The arguments given to the request.
1464
     * @return {Boolean}
1465
     */
1466
    completeMoveCourse: function(transactionid, response, args) {
1467
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1468
            managementconsole = this.get('console'),
1469
            category,
1470
            course,
1471
            totals;
1472
        if (outcome === false) {
1473
            return false;
1474
        }
1475
        course = managementconsole.getCourseById(args.courseid);
1476
        if (!course) {
1477
            return false;
1478
        }
1479
        this.highlight();
1480
        if (course) {
1481
            if (outcome.paginationtotals) {
1482
                totals = managementconsole.get('courselisting').one('.listing-pagination-totals');
1483
                if (totals) {
1484
                    totals.set('innerHTML', outcome.paginationtotals);
1485
                }
1486
            }
1487
            if (outcome.totalcatcourses !== 'undefined') {
1488
                totals = this.get('node').one('.course-count span');
1489
                if (totals) {
1490
                    totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.totalcatcourses));
1491
                }
1492
            }
1493
            if (typeof outcome.fromcatcoursecount !== 'undefined') {
1494
                category = managementconsole.get('activecategoryid');
1495
                category = managementconsole.getCategoryById(category);
1496
                if (category) {
1497
                    totals = category.get('node').one('.course-count span');
1498
                    if (totals) {
1499
                        totals.set('innerHTML', totals.get('innerHTML').replace(/^\d+/, outcome.fromcatcoursecount));
1500
                    }
1501
                }
1502
            }
1503
            course.remove();
1504
        }
1505
        return true;
1506
    },
1507
 
1508
    /**
1509
     * Makes an item visible.
1510
     *
1511
     * @method show
1512
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1513
     * @param {Object} response The response from the AJAX request.
1514
     * @param {Object} args The arguments given to the request.
1515
     * @return {Boolean}
1516
     */
1517
    show: function(transactionid, response, args) {
1518
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1519
            hidebtn;
1520
        if (outcome === false) {
1521
            return false;
1522
        }
1523
 
1524
        this.markVisible();
1525
        hidebtn = this.get('node').one('a[data-action=hide]');
1526
        if (hidebtn) {
1527
            hidebtn.focus();
1528
        }
1529
        if (outcome.categoryvisibility) {
1530
            this.updateChildVisibility(outcome.categoryvisibility);
1531
        }
1532
        if (outcome.coursevisibility) {
1533
            this.updateCourseVisiblity(outcome.coursevisibility);
1534
        }
1535
        this.updated();
1536
    },
1537
 
1538
    /**
1539
     * Hides an item.
1540
     *
1541
     * @method hide
1542
     * @param {Number} transactionid The transaction ID of the AJAX request (unique)
1543
     * @param {Object} response The response from the AJAX request.
1544
     * @param {Object} args The arguments given to the request.
1545
     * @return {Boolean}
1546
     */
1547
    hide: function(transactionid, response, args) {
1548
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1549
            showbtn;
1550
        if (outcome === false) {
1551
            return false;
1552
        }
1553
        this.markHidden();
1554
        showbtn = this.get('node').one('a[data-action=show]');
1555
        if (showbtn) {
1556
            showbtn.focus();
1557
        }
1558
        if (outcome.categoryvisibility) {
1559
            this.updateChildVisibility(outcome.categoryvisibility);
1560
        }
1561
        if (outcome.coursevisibility) {
1562
            this.updateCourseVisiblity(outcome.coursevisibility);
1563
        }
1564
        this.updated();
1565
    },
1566
 
1567
    /**
1568
     * Updates the visibility of child courses if required.
1569
     * @method updateCourseVisiblity
1570
     * @chainable
1571
     * @param courses
1572
     */
1573
    updateCourseVisiblity: function(courses) {
1574
        var managementconsole = this.get('console'),
1575
            key,
1576
            course;
1577
        try {
1578
            for (key in courses) {
1579
                if (typeof courses[key] === 'object') {
1580
                    course = managementconsole.getCourseById(courses[key].id);
1581
                    if (course) {
1582
                        if (courses[key].visible === "1") {
1583
                            course.markVisible();
1584
                        } else {
1585
                            course.markHidden();
1586
                        }
1587
                    }
1588
                }
1589
            }
1590
        } catch (err) {
1591
        }
1592
        return this;
1593
    },
1594
 
1595
    /**
1596
     * Updates the visibility of subcategories if required.
1597
     * @method updateChildVisibility
1598
     * @chainable
1599
     * @param categories
1600
     */
1601
    updateChildVisibility: function(categories) {
1602
        var managementconsole = this.get('console'),
1603
            key,
1604
            category;
1605
        try {
1606
            for (key in categories) {
1607
                if (typeof categories[key] === 'object') {
1608
                    category = managementconsole.getCategoryById(categories[key].id);
1609
                    if (category) {
1610
                        if (categories[key].visible === "1") {
1611
                            category.markVisible();
1612
                        } else {
1613
                            category.markHidden();
1614
                        }
1615
                    }
1616
                }
1617
            }
1618
        } catch (err) {
1619
        }
1620
        return this;
1621
    }
1622
};
1623
Y.extend(Category, Item, Category.prototype);
1624
/**
1625
 * A managed course.
1626
 *
1627
 * @namespace M.course.management
1628
 * @class Course
1629
 * @constructor
1630
 * @extends Item
1631
 */
1632
Course = function() {
1633
    Course.superclass.constructor.apply(this, arguments);
1634
};
1635
Course.NAME = 'moodle-course-management-course';
1636
Course.CSS_PREFIX = 'management-course';
1637
Course.ATTRS = {
1638
 
1639
    /**
1640
     * The course ID of this course.
1641
     * @attribute courseid
1642
     * @type Number
1643
     */
1644
    courseid: {},
1645
 
1646
    /**
1647
     * True if this is the selected course.
1648
     * @attribute selected
1649
     * @type Boolean
1650
     * @default null
1651
     */
1652
    selected: {
1653
        getter: function(value, name) {
1654
            if (value === null) {
1655
                value = this.get('node').getData(name);
1656
                this.set(name, value);
1657
            }
1658
            return value;
1659
        },
1660
        value: null
1661
    },
1662
    node: {
1663
 
1664
    },
1665
    /**
1666
     * The management console tracking this course.
1667
     * @attribute console
1668
     * @type Console
1669
     * @writeOnce
1670
     */
1671
    console: {
1672
        writeOnce: 'initOnly'
1673
    },
1674
 
1675
    /**
1676
     * The category this course belongs to.
1677
     * @attribute category
1678
     * @type Category
1679
     * @writeOnce
1680
     */
1681
    category: {
1682
        writeOnce: 'initOnly'
1683
    }
1684
};
1685
Course.prototype = {
1686
    /**
1687
     * Initialises the new course instance.
1688
     * @method initializer
1689
     */
1690
    initializer: function() {
1691
        var node = this.get('node'),
1692
            category = this.get('category');
1693
        this.set('courseid', node.getData('id'));
1694
        if (category && category.registerCourse) {
1695
            category.registerCourse(this);
1696
        }
1697
        this.set('itemname', 'course');
1698
    },
1699
 
1700
    /**
1701
     * Returns the name of the course.
1702
     * @method getName
1703
     * @return {String}
1704
     */
1705
    getName: function() {
1706
        return this.get('node').one('a.coursename').get('innerHTML');
1707
    },
1708
 
1709
    /**
1710
     * Handles an event relating to this course.
1711
     * @method handle
1712
     * @param {String} action
1713
     * @param {EventFacade} e
1714
     * @return {Boolean}
1715
     */
1716
    handle: function(action, e) {
1717
        var managementconsole = this.get('console'),
1718
            args = {courseid: this.get('courseid')};
1719
        switch (action) {
1720
            case 'moveup':
1721
                e.halt();
1722
                managementconsole.performAjaxAction('movecourseup', args, this.moveup, this);
1723
                break;
1724
            case 'movedown':
1725
                e.halt();
1726
                managementconsole.performAjaxAction('movecoursedown', args, this.movedown, this);
1727
                break;
1728
            case 'show':
1729
                e.halt();
1730
                managementconsole.performAjaxAction('showcourse', args, this.show, this);
1731
                break;
1732
            case 'hide':
1733
                e.halt();
1734
                managementconsole.performAjaxAction('hidecourse', args, this.hide, this);
1735
                break;
1736
            case 'select':
1737
                var c = this.get('console'),
1738
                    movetonode = c.get('courselisting').one('#menumovecoursesto');
1739
                if (movetonode) {
1740
                    if (c.isCourseSelected(e.currentTarget)) {
1741
                        movetonode.removeAttribute('disabled');
1742
                    } else {
1743
                        movetonode.setAttribute('disabled', true);
1744
                    }
1745
                }
1746
                break;
1747
            default:
1748
                return false;
1749
        }
1750
    },
1751
 
1752
    /**
1753
     * Removes this course.
1754
     * @method remove
1755
     */
1756
    remove: function() {
1757
        this.get('console').removeCourseById(this.get('courseid'));
1758
        this.get('node').remove();
1759
    },
1760
 
1761
    /**
1762
     * Moves this course after another course.
1763
     *
1764
     * @method moveAfter
1765
     * @param {Number} moveaftercourse The course to move after or 0 to put it at the top.
1766
     * @param {Number} previousid the course it was previously after in case we need to revert.
1767
     */
1768
    moveAfter: function(moveaftercourse, previousid) {
1769
        var managementconsole = this.get('console'),
1770
            args = {
1771
                courseid: this.get('courseid'),
1772
                moveafter: moveaftercourse,
1773
                previous: previousid
1774
            };
1775
        managementconsole.performAjaxAction('movecourseafter', args, this.moveAfterResponse, this);
1776
    },
1777
 
1778
    /**
1779
     * Performs the actual move.
1780
     *
1781
     * @method moveAfterResponse
1782
     * @protected
1783
     * @param {Number} transactionid The transaction ID for the request.
1784
     * @param {Object} response The response to the request.
1785
     * @param {Objects} args The arguments that were given with the request.
1786
     * @return {Boolean}
1787
     */
1788
    moveAfterResponse: function(transactionid, response, args) {
1789
        var outcome = this.checkAjaxResponse(transactionid, response, args),
1790
            node = this.get('node'),
1791
            previous;
1792
        if (outcome === false) {
1793
            previous = node.ancestor('ul').one('li[data-id=' + args.previous + ']');
1794
            if (previous) {
1795
                // After the last previous.
1796
                previous.insertAfter(node, 'after');
1797
            } else {
1798
                // Start of the list.
1799
                node.ancestor('ul').one('li').insert(node, 'before');
1800
            }
1801
            return false;
1802
        }
1803
        this.highlight();
1804
    }
1805
};
1806
Y.extend(Course, Item, Course.prototype);
1807
 
1808
 
1809
}, '@VERSION@', {
1810
    "requires": [
1811
        "base",
1812
        "node",
1813
        "io-base",
1814
        "moodle-core-notification-exception",
1815
        "json-parse",
1816
        "dd-constrain",
1817
        "dd-proxy",
1818
        "dd-drop",
1819
        "dd-delegate",
1820
        "node-event-delegate"
1821
    ]
1822
});